Undefined index notice in php regexp - php

Well, I've tried searching over the internet for a few hours now, but so far it's been of no assistance. I am trying to figure out why I keep getting the following notice notice(yes, I am aware it is just a notice and does not halt the execution of the script):
Notice: Undefined index: 05 in [redacted_path]\script.php(31) : regexp code on line 1
This script was meant as a color re-placer for irc channel logs; I've grabbed the large function from another site and modified it slightly to suit my needs. Credit of course goes to its creator. That aside, I am.. lightly familiar with regex, but not the point where I can of course spot the offending typo or similar below. Any assistance would be appreciated.
<?php
if (isset($_POST['chn'])) {
$chn = $_POST['chn'];
$str = urldecode($_POST['line']);
$filen = "logs/$chn" . "_" . date('Y_F_d') . ".html";
$str = preg_replace('/https?:\/\/[^\s"<>]+/', '$0', $str);
$logln = mirc2html($str) . "<br />\r\n";
$logln = str_replace("Â","",$logln);
file_put_contents($filen,stripcslashes($logln),FILE_APPEND);
}
if (isset($_GET['test'])) {
echo str_replace("Â","",mirc2html($_GET['test']));
}
function mirc2html($x) {
$c = array("FFF","000","00007F","009000","FF0000","7F0000","9F009F","FF7F00","FFFF00","00F800","00908F","00FFFF","0000FF","FF00FF","7F7F7F","CFD0CF");
$x = preg_replace("/\x02(.*?)((?=\x02)\x02|$)/", "<b>$1</b>", $x);
$x = preg_replace("/\x1F(.*?)((?=\x1F)\x1F|$)/", "<u>$1</u>", $x);
$x = preg_replace("/\x1D(.*?)((?=\x1D)\x1D|$)/", "<i>$1</i>", $x);
$x = preg_replace("/\x03(\d\d?),(\d\d?)(.*?)(?(?=\x03)|$)/e", "'<span style=\"color: #'.\$c['$1'].'; background-color: #'.\$c['$2'].';\">$3</span>'", $x, -1, $n1);
$x = preg_replace("/\x03(\d\d?)(.*?)(?(?=\x03)|$)/e", "'</span><span style=\"color: #'.\$c['$1'].';\">$2</span>'", $x);
$x = preg_replace("/\x03|\x0F(.*?)/", "<span style=\"color: #000; background-color: #FFF;\">$1</span>", $x);
$x = str_replace("\n","",$x);
$x = str_replace("\r","",$x);
$x = preg_replace("/\<\/span\>/","",$x,1);
$x = preg_replace("/(\<\/span\>){2}/","</span>",$x);
return $x;
}
?>
The offending line is thus:
$x = preg_replace("/\x03(\d\d?)(.*?)(?(?=\x03)|$)/e", "'</span><span style=\"color: #'.\$c['$1'].';\">$2</span>'", $x);

The cause of your error is clear: The variable $c['05'] is not defined. Define it or change the replacement with a function that is able to convert it into an integer that - for some reason I'm not clear about - PHP is not able with that eval'ed regex replacement (normally numerical strings are converted into integer for keys automatically internally).
If you're open for some suggestions:
Refactor the code to remove the duplicate parts (It's always some code that does something, multiple classes of codes, each class has it's own replacements).
Do the replacement with a callback function so that you can better control the replacement and pass the color values.

Related

Incrementing a string by one in PHP

I am struggling to find a way to increment a specific pattern required. For each new member, they are given a unique ID such as ABC000001. Each new members ID should increment by one. So the second member's ID would be ABC000002. I am using PHP and MySQL to keep track of each member but I have not been able to come up with a way to properly increment using the string format above.
What is the best way to approach this?
As #axiac mentions this is probably not a good idea but it's pretty easy to manage.
$memberid = 'ABC000001';
list($mem_prefix,$mem_num) = sscanf($memberid,"%[A-Za-z]%[0-9]");
echo $mem_prefix . str_pad($mem_num + 1,6,'0',STR_PAD_LEFT);
Split your current member number into the alpha and numeric parts then put them back together bumping the number when you do it. I use this as a function and pass the previous ID and what I get back is the next ID in the sequence.
You can extract only digits using regex to increment and using str_pad for create a prefix :
$memberid = 'ABC000001';
$num = preg_replace('/\D/', '',$memberid);
echo sprintf('ABC%s', str_pad($num + 1, "6", "0", STR_PAD_LEFT));
Possible answer without regex.
Runs through each character and checks if it is a number or not.
Then uses sprintf() to make sure leading 0s are still there.
$str = "ABC000001";
$number = "";
$prefix = "";
$strArray = str_split($str);
foreach ($strArray as $char) {
if (is_numeric($char)) {
$number .= $char;
} else {
$prefix .= $char;
}
}
$length = strlen($number);
$number = sprintf('%0' . $length . 'd', $number + 1);
echo $prefix . $number;
This works for this instance but would not work if the prefix had numbers in it.
just use the PHP increment operator ++
as long as you've sufficient leading zeros in the pattern, then PHP will correctly increment the numeric component
This code:
<?php
$name = 'ABC0000098';
print ++$name . PHP_EOL;
print ++$name . PHP_EOL;
print ++$name . PHP_EOL;
Outputs:
ABC0000099
ABC0000100
ABC0000101
Read more in the PHP docs:
https://www.php.net/manual/en/language.operators.increment.php
aha - just saw there is already a link to a similar example in the comment posted by "axiac" above

Remove the 0 from for results

Currently, I'm messing around with SoundCloud's API and it's returning something that looks like this.
0. HotBox Michael da Vinci Prod Free P.mp3
https://api.soundcloud.com/tracks/373337717/stream?client_id=OURID
1. LSSR Chris P Prod Jake Knight.mp3
https://api.soundcloud.com/tracks/373336760/stream?client_id=OURID
Which is working properly as how the code appears, here it is how I'm printing out the results (very messy but I'm just messing around)
for ($i = 0; isset($house[$i]); $i++) {
$b1 = $house[$i]['title'];
$b2 = $house[$i]['stream_url'];
print '<br>'; print '<br>';
$title = preg_replace('/[^a-zA-Z0-9\s]/', '', strip_tags($b1));
print ''.$i.'. '.$title.'.mp3';
print '<br>';
print $stream = ''.$b2.'?client_id=OURID';
}
Now what I'm wondering is how under every circumstance can we make the 0. return as a 1. and continue counting upwards until reaching the end of the for loop, not removing the 0. data but only changing the number.
I recommend a foreach loop with the $i counter declared in the loop and just increment it as you go.
Untested code:
foreach ($house as $i=>$row){
echo '<br><br>',++$i,'. ',preg_replace ('/[^a-z\d\s]+/i','',strip_tags ($row ['title'])),".mp3<br>{$row ['stream_url']}?client_id=OURID";
}
p.s. I've improved the regex pattern and eliminated all single-use variable declarations. You can break this up over many lines if you prefer.
If you want to avoid the first iteration's double break tags...
foreach ($house as $i=>$row){
if($i) echo '<br><br>'; // if $i is not 0
echo ++$i,'. ';
echo preg_replace ('/[^a-z\d\s]+/i','',strip_tags ($row ['title']));
echo ".mp3<br>{$row ['stream_url']}?client_id=OURID";
}
Based on the comments and your description of what you are trying to do, it sounds like you just need to add another iteration variable:
# Add another variable
$a = 1;
for ($i = 0; isset($house[$i]); $i++) {
$b1 = $house[$i]['title'];
$b2 = $house[$i]['stream_url'];
$title = preg_replace('/[^a-zA-Z0-9\s]/', '', strip_tags($b1));
echo '<br><br>';
# Here you have the $a show up starting at 1
echo ''.$a.'. '.$title.'.mp3<br>';
echo $stream = ''.$b2.'?client_id=OURID';
# Auto increment here
$a++;
}

What is the backend difference between "filter_var" and "preg_replace" in PHP?

I have numbers coming out of a database (very controlled input) that will have underscores before and after them. They are stored like this:
_51_ _356_
They will not be stored in any other format, but there will be times where I need to get just the numbers out of them. I have chosen to use either
$x = filter_var($myNumber, FILTER_SANITIZE_NUMBER_INT);
or
$y = preg_replace("/[^0-9]/","",$myNumber);
I am not sure of the nuances between the 2 in the backend, but they both produce exactly what I need (I think so, anyway), so it doesn't matter to me which I use. What are the pros and cons of using each of these options? (For example, does one use an array or other weird thing that I might need to know about? One uses way too many resources?)
Well, there isn't big difference in your case. I think preg_replace is more expensive in resource, since it had to parse the regex pattern.
Alternatively you can use trim:
echo trim('_12_', '_');
It will remove the '_' in both side, I think this is the most readable manner to do.
Filters don't use regular expressions, but work in a similar way: iterate a string char-by-char and remove characters that don't match the pattern:
for (i = 0; i < Z_STRLEN_P(value); i++) {
if ((*map)[str[i]]) {
buf[c] = str[i];
++c;
}
}
#http://lxr.php.net/xref/PHP_5_6/ext/filter/sanitizing_filters.c#filter_map_apply
and the FILTER_SANITIZE_NUMBER_INT is defined as [^0-9+-]:
/* strip everything [^0-9+-] */
const unsigned char allowed_list[] = "+-" DIGIT;
filter_map map;
filter_map_init(&map);
filter_map_update(&map, 1, allowed_list);
filter_map_apply(value, &map);
#http://lxr.php.net/xref/PHP_5_6/ext/filter/sanitizing_filters.c#php_filter_number_int
Of course, [^0-9+-] is not a right expression to filter integer numbers, therefore be prepared for surprises:
$x = filter_var("+++123---", FILTER_SANITIZE_NUMBER_INT);
var_dump($x); // WTF?
My suggestion is to stick to regular expressions: they are explicit and far less buggy than filters.
I wanted to try some various methods for this, so set up the following benchmark. It looks like for your case, trim is definitely the best option as it only has to look at the beginning and end of the string instead of each character. Here are my test results on 10,000,000 random integers surrounded by underscores running PHP 7.0.18.
preg_replace: 1.9469740390778 seconds.
filter_var: 1.6922700405121 seconds.
str_replace: 0.72129797935486 seconds.
trim: 0.37275195121765 seconds.
And here is my code if anyone wants to run similar tests:
<?php
$ints = array();//array_fill(0, 10000000, '_1029384756_');
for($i = 0; $i < 10000000; $i++) {
$ints[] = '_'.mt_rand().'_';
}
$start = microtime(true);
foreach($ints as $v) {
preg_replace('/[^0-9]/', '', $v);
}
$end = microtime(true);
echo 'preg_replace in '.($end-$start).' seconds.',PHP_EOL;
$start = microtime(true);
foreach($ints as $v) {
filter_var($v, FILTER_SANITIZE_NUMBER_INT);
}
$end = microtime(true);
echo 'filter_var in '.($end-$start).' seconds.',PHP_EOL;
$start = microtime(true);
foreach($ints as $v) {
str_replace('_', '', $v);
}
$end = microtime(true);
echo 'str_replace in '.($end-$start).' seconds.',PHP_EOL;
$start = microtime(true);
foreach($ints as $v) {
trim($v, '_');
}
$end = microtime(true);
echo 'trim in '.($end-$start).' seconds.',PHP_EOL;

Turning an integer into random string and back again

what I'm wanting is to convert an integer into a string. For example, 123456789 may become 8GFsah93r ... you know like Youtube, Pastebin and what not. I then want to convert it back.
I'm working with large integers, for example: 131569877435989900
Take a look at this link: http://codepad.viper-7.com/wHKOMi
This is my attempt using a function I found on the web, obviously... it's not correctly converting back to integer. I'm needing something that does this realiably.
Thanks
Ok, one of the ideas is to use a character array as a representation of a numeric system. Then you can convert from base 10 to base x and vica-versa. The value will be shorter and less readable (altought, you should encrypt it with a two-way crypter if it must be secure).
A solution:
final class UrlShortener {
private static $charfeed = Array(
'a','A','b','B','c','C','d','D','e','E','f','F','g','G','h','H','i','I','j','J','k','K','l','L','m',
'M','n','N','o','O','p','P','q','Q','r','R','s','S','t','T','u','U','v','V','w','W','x','X','y','Y',
'z','Z','0','1','2','3','4','5','6','7','8','9');
public static function intToShort($number) {
$need = count(self::$charfeed);
$s = '';
do {
$s .= self::$charfeed[$number%$need];
$number = floor($number/$need);
} while($number > 0);
return $s;
}
public static function shortToInt($string) {
$num = 0;
$need = count(self::$charfeed);
$length = strlen($string);
for($x = 0; $x < $length; $x++) {
$key = array_search($string[$x], self::$charfeed);
$value = $key * pow($need, $x);
$num += $value;
}
return $num;
}
}
Then you can use:
UrlShortener::intToShort(2);
UrlShortener::shortToInt("b");
EDIT
with large numbers, it does not work. You should use this version (with bcmath http://www.php.net/manual/en/book.bc.php ) with very large numbers:
final class UrlShortener {
private static $charfeed = Array(
'a','A','b','B','c','C','d','D','e','E','f','F','g','G','h','H','i','I','j','J','k','K','l','L','m',
'M','n','N','o','O','p','P','q','Q','r','R','s','S','t','T','u','U','v','V','w','W','x','X','y','Y',
'z','Z','0','1','2','3','4','5','6','7','8','9');
public static function intToShort($number) {
$need = count(self::$charfeed);
$s = '';
do {
$s .= self::$charfeed[bcmod($number, $need)];
$number = floor($number/$need);
} while($number > 0);
return $s;
}
public static function shortToInt($string) {
$num = 0;
$need = count(self::$charfeed);
$length = strlen($string);
for($x = 0; $x < $length; $x++) {
$key = array_search($string[$x], self::$charfeed);
$value = $key * bcpow($need, $x);
$num += $value;
}
return $num;
}
}
$original = 131569877435989900;
$short = UrlShortener::intToShort($original);
echo $short;
echo '<br/>';
$result = UrlShortener::shortToInt($short);
echo $result;
echo '<br/>';
echo bccomp($original, $result);
If something missing from here, please let me know, because it's only a snippet from my library (I don't wanna insert the whole thing here)
negra
check base64 encoding: http://php.net/manual/en/function.base64-encode.php http://php.net/manual/en/function.base64-decode.php
If you want a shorter string first encode it into an 8bit string then encode. You can do this with % 256 and / 256.
Or you could manually do what base64 does, get the first 6bits and encode it to a char.
Why not use something like this? Do you need it heavily encrypted?
$num = 131569877435989900;
echo $str = base64_encode($num);
echo base64_decode($str);
I think what you want is to encode the ids using Base32. The resulting string contains only the 26 letters of the alphabet and the digits 2-7, making it very human readable.
The simplest would be to use something like base_convert -- unfortunately, it won't work for such large integers correctly.
However, you can use the same idea by copying base_convert_arbitrary from my answer here and doing:
$id = '131569877435989900';
$encoded = base_convert_arbitrary($id, 10, 36);
$decoded = base_convert_arbitrary($encoded, 36, 10);
print_r($encoded);
print_r($decoded);
See it in action.
The nice thing about this approach is that you can tweak the first line inside the function, which reads:
$digits = '0123456789abcdefghijklmnopqrstuvwxyz'; // 36 "digits"
Add any other "digits" you find acceptable (e.g. capital letters or other symbols you don't mind having in your URL). You can then replace the base 36 in the above example with a larger one (you can go as high as there are defined digits), and it will work just like you want it to.
See it here working with 62 digits.
I am suprised No one is mentioning base64_encode() and it partner base64_decode().
If you were not considering length this is perfect
$before = base64_encode(131569877435989900);
$after = 'MS4zMTU2OTg3NzQzNTk5RSsxNw==';
$on_reverse = base64_decode('MS4zMTU2OTg3NzQzNTk5RSsxNw==');
$on_reverse == 131569877435989900;
I always go for the simplest solutions, as long as they don't compromise my security.
The easiest way to get random string is to use hash functions like md5() or sha1() For example:
<?php
$bigInt = '131569877435989900';
$hash = md5($bigInt);
$hashed=substr($hash,0,-20);
echo $hashed;
?>
These hash functions are irreversible-you can't get the original value(these functions are also used to crypt data). If you want you can save the original big integer in an array or a database. But decripting the hash would be impossible.

How do I "add" strings in PHP - not concatenate, but "add" them. (as if each character was a base 36 numbers)

Unfortunately I inherited some code (c/c++) that does some string manipulation and now I need to copy/port that over to php so this functionality can be accessed over the internets.
Specifically the functionality takes some arbitrary strings and "adds" them together. (the c code iterates down the character array and then does some checking to make sure they are in the alphanumeric range)
I can't find specific code examples on how to do this (I am not a PHP developer) - can anyone point me to some resources that will explain this? (basically how to do string/character array manipulation)
EDIT
In response to some comments and answers:
I want the result in ascii, but essentially I will be adding base 36 numbers.
The C code right now converts to base 36 (from ascii)
then "adds" each element together (does not carry - although the original author intended that - and it for some strange reason does the "add" from most significant to least)
Then converts back to ascii.
Strings can be of different lengths
Based on the current answers i think I have enough of what I need. It is always frustrating sometimes learning a new language - you know exactly what you want and you can do it in other languages, just not the one that is for the task at hand...
Thanks for the responses so far.
Can't you just base_convert() them?
$sum = base_convert($str1, 36, 10) + base_convert($str2, 36, 10);
$sum36 = base_convert($sum, 10, 36);
Or do you need arbitrary precision? Here's a stab at arbitrary precision addition, in base 36:
function b36_add($str1, $str2)
{
$to10 = array();
for ($i = 0; $i < 36; ++$i)
{
$to10[base_convert($i, 10, 36)] = $i;
}
$len = max(strlen($str1), strlen($str2));
$str1 = str_repeat('0', $len - strlen($str1)) . $str1;
$str2 = str_repeat('0', $len - strlen($str2)) . $str2;
$pos = $len - 1;
$carry = 0;
$sum = '';
do
{
$tmp = base_convert($carry + $to10[$str1[$pos]] + $to10[$str2[$pos]], 10, 36);
$sum .= substr($tmp, -1);
$carry = (int) substr($tmp, 0, -1);
}
while (--$pos >= 0);
$sum = strrev($sum);
if ($carry)
{
$sum = base_convert($carry, 10, 36) . $sum;
}
return $sum;
}
If you have a string like this in php you can just call the index of an individual character like so:
<?
$x = "Hello";
print $x[0] . "\n";
So in other words, $string_var[n] gives you the nth char, 0-indexed.
First off, I'm assuming you want to add ascii values.
ord() might help you. Based on the other answer, something like:
<?php
function addStrings($x, $y){
// Assumes that both strings are the same length
for($i=0; $i<strlen($x); $i++){
$result[i] = ord($x[i]) + ord($y[i]);
}
return $result;
}
?>
If you use this, you'll probably want to do something if $x and $y are different lengths, but I think it gets the idea across.

Categories