PHP rename files, starting with special name? - php

i wanted to rename files from databse. so..i wrote bellow.
it works fine except for names with long length int.
(ex: bartmp_9404865346.jpg does not work but bartmp_585558.jpg is working)
$subject = '[img]http://www.example.org/users/uploads/bartmp_9404865346.jpg[/img]
Hello world
[img]http://www.example.org/users/uploads/bartmp_585558.jpg[/img]';
preg_match_all('/\[img\](.*?)\[\/img\]/', $subject, $files);
foreach ($files[1] as $file) {
$n = sscanf($file, "http://www.example.org/users/uploads/bartmp_%d.jpg");
$refile = sprintf("http://www.example.org/users/uploads/mybar_%d.jpg", $n[0]);
rename($file, $refile);
}
can you give me any alter way to do this or a little hint to modify this.
thanks.

The %d format specifier only accepts numbers that would fit in an integer (depending on the platform that would be 2^31 or 2^63); without losing precision, in this case, a regular expression may work better:
if (preg_match('#^http://www.example.org/users/uploads/bartmp_(\d+)\.jpg$#', $file, $matches)) {
$refile = sprintf('http://www.example.org/users/uploads/mybar_%s.jpg', $matches[1]);
rename($file, $refile);
}
The above expression matches only digits but stores the match as a string value, so it won't lose numeric precision.

You are using %d for decimal which seems superficially correct:
$n = sscanf($file, "http://www.example.org/users/uploads/bartmp_%d.jpg");
$refile = sprintf("http://www.example.org/users/uploads/mybar_%d.jpg", $n[0]);
The issue is the max numerical value in PHP—and other languages compiled as 32-bit—is 2147483647 so 9404865346 won’t fly. Instead you should look to extract the value as a string instead like this:
$n = sscanf($file, "http://www.example.org/users/uploads/bartmp_%s.jpg");
$refile = sprintf("http://www.example.org/users/uploads/mybar_%s", $n[0]);

Related

Workaround needed, PHP dechex maximum integer [duplicate]

I have some large HEX values that I want to display as regular numbers, I was using hexdec() to convert to float, and I found a function on PHP.net to convert that to decimal, but it seems to hit a ceiling, e.g.:
$h = 'D5CE3E462533364B';
$f = hexdec($h);
echo $f .' = '. Exp_to_dec($f);
Output: 1.5406319846274E+19 = 15406319846274000000
Result from calc.exe = 15406319846273791563
Is there another method to convert large hex values?
As said on the hexdec manual page:
The function can now convert values
that are to big for the platforms
integer type, it will return the value
as float instead in that case.
If you want to get some kind of big integer (not float), you'll need it stored inside a string. This might be possible using BC Math functions.
For instance, if you look in the comments of the hexdec manual page, you'll find this note
If you adapt that function a bit, to avoid a notice, you'll get:
function bchexdec($hex)
{
$dec = 0;
$len = strlen($hex);
for ($i = 1; $i <= $len; $i++) {
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
}
return $dec;
}
(This function has been copied from the note I linked to; and only a bit adapted by me)
And using it on your number:
$h = 'D5CE3E462533364B';
$f = bchexdec($h);
var_dump($f);
The output will be:
string '15406319846273791563' (length=20)
So, not the kind of big float you had ; and seems OK with what you are expecting:
Result from calc.exe =
15406319846273791563
Hope this help ;-)
And, yes, user notes on the PHP documentation are sometimes a real gold mine ;-)
hexdec() switches from int to float when the result is too large to be represented as an int. If you want arbitrarily long values, you're probably going to have to roll your own conversion function to change the hex string to a GMP integer.
function gmp_hexdec($n) {
$gmp = gmp_init(0);
$mult = gmp_init(1);
for ($i=strlen($n)-1;$i>=0;$i--,$mult=gmp_mul($mult, 16)) {
$gmp = gmp_add($gmp, gmp_mul($mult, hexdec($n[$i])));
}
return $gmp;
}
print gmp_strval(gmp_hexdec("D5CE3E462533364B"));
Output: 15406319846273791563
$num = gmp_init( '0xD5CE3E462533364B' ); // way to input a number in gmp
echo gmp_strval($num, 10); // display value in decimal
That's the module to use. Convert it to a function and then use on your numbers.
Note: provide these hex numbers as strings so:
$num = "0x348726837469972346"; // set variable
$gmpnum = gmp_init("$num"); // gmp number format
echo gmp_strval($gmpnum, 10); // convert to decimal and print out
1.5406319846274E+19 is a limited representation of you number. You can have a more complete one by using printf()
printf("%u\n", hexdec($h));
...will output "15406319846273792000". PHP uses floats for such big numbers, so you may lose a bit of precision. If you have to work with arbitrary precision numbers, you may try the bcmath extension. By splitting the hex into two 32-bit words (which should be safe on most systems) you should be able to get more precision. For instance:
$f = bcadd(bcmul(hexdec(substr($h, 0, -8)), 0x100000000), hexdec(substr($h, 8)));
...would set $f to 15406319846273791563.
Convert HEX to DEC is easy.. But, reconstruct back hexadecimal number is very hard.
Try to use base_convert ..
$hexadecimal = base_convert(2826896153644826, 10, 16);
// result: a0b0c0d0e0f1a
Run into this issue while storing 64-bit keys in MySQL database. I was able to get a bit perfect conversion to a 64-bit signed integer (PHP limitation) using a few binary operators: (This code is 16x faster than bchexdec function and resulting variables are using half the memory on average).
function x64toSignedInt($k){
$left = hexdec(substr($k,0,8));
$right = hexdec(substr($k,8,8));
return (int) ($left << 32) | $right;
}
MySQL signed BIGINT datatype is a great match for this as an index or storage in general. HEX(column) is a simple way to convert it back to HEX within the SQL query for use elsewhere.
This solution also uses the BC Math Functions. However, an algorithm is used which does without the bcpow function. This function is a bit shorter and faster than the accepted solution, tested on PHP 7.4.
function hexDecBc(string $hex) : string
{
for ($dec = '0', $i = 0; $i < strlen($hex); $i++) {
$dec = bcadd(bcmul($dec,'16'),(string)hexdec($hex[$i]));
}
return $dec;
}
Make sure to enable gmp extension. ext-gmp
$number = gmp_strval(gmp_init('0x03....')); // outputs: 1234324....
Doesn't intval(var, base) take care of it?
From the PHP Manual.

Writing Binary Data from Hex String in PHP

I'm trying to write a binary file from hex string.
For example, if my hex string is C27EF0EC, then the hex file should contain ASCII characters for C2, 7E, F0 and EC.
How do I do this in PHP?
Here's what I've tried:
$s="";
for ($i=0; $i<count($h); $i++) {
$s+=pack("C*", "0x".$h[$i]);
}
$f2=fopen("codes0", "wb+");
fwrite($f2, $s);
So the first thing you need to do is turn your single string into an array of two-character strings with str_split.
$hex_bytes = str_split($h, 2);
Then you want to convert each of those values from a hexadecimal string to the corresponding number with hexdec.
$code_array = array_map(hexdec, $hex_bytes);
Then you want the byte value corresponding to each of those character codes, which you can get with chr:
$char_array = array_map(chr, $code_array);
Finally, you want to join all those bytes together into a single string, which you can do with implode.
$s = implode($char_array);
You can use the steps above in that order, or you can put it all together into one expression like this:
$s = implode(array_map(chr, array_map(hexdec, str_split($h,2))));
Note that as soon as you get a value above 0x7F it's no longer "ASCII".
Assuming that array $binary is a previously constructed array bytes (like monochrome bitmap pixels in my case) that you want written to the disk in this exact order, the below code worked for me on an AMD 1055t running ubuntu server 10.04 LTS.
I iterated over every kind of answer I could find on the Net, checking the output (I used either shed or vi, like in this answer) to confirm the results.
<?php
$fp = fopen($base.".bin", "w");
$binout=Array();
for($idx=0; $idx < $stop; $idx=$idx+2 ){
if( array_key_exists($idx,$binary) )
fwrite($fp,pack( "n", $binary[$idx]<<8 | $binary[$idx+1]));
else {
echo "index $idx not found in array \$binary[], wtf?\n";
}
}
fclose($fp);
echo "Filename $base.bin had ".filesize($base.".bin")." bytes written\n";
?>

Handling large numbers in PHP

After a couple of minutes i've realize the bug that i was having: the magic 2147483647 number, that upper limit for integer type on PHP/32. I need to manage biggers number in my function:
public function albumExists($name) // e.g. 104112826372452
{
$albums = $this->getAlbums();
// If $name is int, search the key in $albums
if(is_int($name) && ($found = array_key_exists($id = intval($name), $albums)))
return ($found ? $id : false);
// Start looking for $name as string
foreach($album as $id => $a) if ($a->name == $name) return intval($id);
return false; // Found nothing
}
in order to give the ability to search both by id and name. But intval() will always return the upper limit. How can handle quite big numbers like, say, 104112826372452? Ideas?
EDIT: usage example:
$album = $fb->createAlbum('Test Album'); // Will return album id
// The use albumExists to check if id exists
$photo1 = $fb->uploadPhoto('mypic1.png', null, $album);
$photo2 = $fb->uploadPhoto('mypic2.png', null, 'Test Album'); // find or create
If you're converting to an int for sanity purposes (so it appears), perhaps you could just adjust it to evaluate it purely on it's numeric basis instead of int datatype:
if(ctype_digit($name) && ($found = array_key_exists($id = $name, $albums)))
return ($found ? $id : false);
//etc
Actually, should this work too?
if(ctype_digit($name) && ($found = array_key_exists($name, $albums)))
return ($found ? $name: false);
//etc
As workaround you can use the gmp or bcmath functions for that.
It's not quite clear why you insist on casting to PHP integers. Just leave your database numbering as strings, when don't need to calculate with them. Not everything that looks like a number needs to be represented as number.
I guess your real problem is the differentation with is_int(). Just use is_numeric() in its place, which works with arbitrary-length numeric strings and does not depend on integer-casted values.
An int has an upper limit, and bigger numbers will be represented as floats, which are imprecise and therefore a bad idea to use in this situation. Use a string to store such numbers and the BC Math extension if you need to do calculations on it.
Unfornuately PHP int type can only go upto 2147483647 but PHP float can hold integers upto 10000000000000
Check out php.net
http://php.net/manual/en/language.types.integer.php
UPDATE
PHP.net says that a float can accurately hold an integer upto 10000000000000. Im not sure if float has an upper limit though.
One option is to run PHP on a 64bit OS as the in size is determined by the underlying operating system. This is obviously dependent if you can get access to 64bit hardware, one thing to note is that this will be faster than using gmp/bcmath but unless pure speed is your aim it probably won't be an issue for you

Random number/letter value

So I was wonder what are some good/preferred methods for generating a 'hex-like' value in PHP? Preferably, I would want to restrict it to 5 characters long like such: 1e1f7
Currently this is what I am doing:
echo dechex(mt_rand(10000, 99999));
however this gives me values anywhere from 4-5 characters long, and I want to keep it at a consistent 4 or 5.
What are some ways to better generate something like this in PHP? Is there even a built in function?
Note: When I say 'hex-like' I really just mean a random combination of letters and numbers. There does not have to be a restriction on available letters.
Something simple like:
$length = 5;
$string = "";
while ($length > 0) {
$string .= dechex(mt_rand(0,15));
$length -= 1;
}
return $string;
(untested)
Or fix your mt_rand range to: mt_rand(65535, 1048575) (10000-fffff in hex) or if you like tinfoil hats: mt_rand(hexdec("10000"), hexdec("ffffff"))
The advantage of the while-loop approach is that it works for arbitrarily long strings. If you'd want 32 random characters you're well over the integer limit and a single mt_rand will not work.
If you really just want random stuff, I'd propose:
$length = 5;
$string = "";
$characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=+!##$%^&*()[]"; // change to whatever characters you want
while ($length > 0) {
$string .= $characters[mt_rand(0,strlen($characters)-1)];
$length -= 1;
}
return $string;
(untested)
echo substr( base64_encode( mt_rand(1000, mt_getrandmax() ), 0, 5);
This uses more of the alphabet due to the base64, but remember that it will include upper and lower case letters along with numbers.
Why all the work sha1 is tested and evenly distributed:
substr(sha1(uniqid('moreentropyhere')),0,5);
I have used this to generate millions and millions of uniq uids for sharding tables, no collisions and remarkably evenly distributed regardless of the length you use...
you can even use binary form of sha1 hash for base 64:
base64_encode(sha1(uniqid('moreentropyhere'), true))
to limit characters, you can use a regex:
substr(preg_replace('~[^a-km-np-z2-9]~','',strtolower(base64_encode(sha1(uniqid(),true)))),0,6)
Here we limited 0,1,l (letter), and o (letter) from the string, trading a little entropy to prevent confusion (and service tickets) during entry for all ages...

PHP - read 8 bit integers

I have a binary file that is all 8 bit integers. I have tried to use the php unpack() functions but I cant get any of the arguments to work for 1 byte integers. I have tried to combine the data with a dummy byte so that I can use the 'n'/'v' arguments. I am working with a windows machine to do this. Ultimately I would like a function to return an array of integers based on a string of 8 bit binary integers. The code I have tried is below -
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = fread($dat_file, 1);
$dummy = decbin(0);
$combined = $dummy.$dat_data;
$result = unpack("n", $combined);
What your looking for is the char datatype. Now there are two version of this, signed (lowercase c) and unsigned (uppercase C). Just use the one that's correct for your data.
<?php
$byte = unpack('c', $byte);
?>
Also, if the data file is just a bunch of bytes and nothing else, and you know it's length, you can do this. (If the length is 16 signed chars in a row.)
<?php
$bytes = unpack('c16', $byte);
?>
If you don't know how many bytes will be in the file, but you know there is only going to be bytes you can use the asterisk code to read until EOF.
<?php
$bytes = unpack('c*', $byte);
?>
The following should do what you want (ord):
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = ord(fread($dat_file, 1));
What you are trying to do is retrieve the integer value of the single byte. Because you are reading in single bytes at a time, you will always have exactly one valid ASCII character. ord returns the binary value of that one character.

Categories