I have some strange behavior with the unpack function. I have a packed string, stored as longblob in a mysql database. When I read that string and unpack it, it gives me an array, so far so good. But when I run this on another machine some of the values in the array are different.
When I dump the data from mysql, they are equal on both machines.
Unpacking is done this way:
$array = unpack("N*", $packed);
$array should then be like this (and it is on one machine)
Array
(
[1] => 179848175
[2] => -16214255
[3] => 179848175
[4] => -16214255
[5] => 179848175
[6] => -16214255
[7] => 179999949
[8] => -16152916
[9] => 179999277
[10] => -16168574
...
)
But on the other machine it is like this:
Array
(
[1] => 179848175
[2] => 427853622
[3] => 179848175
[4] => 427853622
[5] => 179848175
[6] => 427853622
[7] => 179999949
[8] => 427853423
[9] => 179999277
[10] => 427853341
...
)
Every second value seems to be different.
I have tested this on three different machines, on two everything was fine, but on that one machine I get that weird output.
One machine is running PHP 5.6.3 (here it is ok), two machines are running PHP 5.5.14 (on one it is ok, on the other not)
The pack format N means unsigned long, which means it can't be negative. However, you are storing negative values, and they are the ones that aren't being unpacked the way you want. PHP does not have a pack format for machine-independent signed longs; it only supports packing them in machine byte order, which may not be compatible from machine to machine. Thus you'll have to make the values signed yourself.
To convert your array items into signed values:
for ($i = 1; $i <= count($array); $i++) {
// Check for a greater-than-32-bit environment,
// and check if the number should be negative
// (i.e., if the high bit is set in 32-bit notation).
if (PHP_INT_SIZE > 4 && $array[$i] & 0x80000000) {
// A negative number was unpacked as an unsigned
// long in a greater-than-32-bit environment.
// Subtract the appropriate amount (max 32-bit
// unsigned long + 1) to convert it to negative.
$array[$i] = $array[$i] - 0x100000000;
}
}
var_dump($array);
Related
So I'm learning Php, so as I was messing around with arrays to see how they work, I stumbled into this when I made two arrays.
$TestArray1 = array( 1 => 1, "string" => "string", 24, "other", 2 => 6, 8);
$TestArray2 = array( 6 => 1, "string" => "string", 24, "other", 1 => 6, 8);
But when I print them out with print_r() this is what I get (this also happens with var_dump by the way)
Array ( [1] => 1 [string] => string [2] => 6 [3] => other [4] => 8 )
Array ( [6] => 1 [string] => string [7] => 24 [8] => other [1] => 6 [9] => 8 )
As far as I can tell, by putting the two in the second array it overwrites the next possible spot with no key and then keeps going, shortening the array. So I thought that meant that if I use a 1 it would put it at the start but that does not happen either.
Is this normal or is there something wrong with my php installation?
Im using Ampps in windows 10 with php 7.3.
Thanks in advance
Good question.
What's happening is that when determining automatic numeric indexes, PHP will look to the largest numeric index added and increment it (or use 0 if there are none).
The key is optional. If it is not specified, PHP will use the increment of the largest previously used integer key.
What's happening with your first array is that as it is evaluated left-to-right, 24 is inserted at index 2 because the last numeric index was 1 => 1.
Then when it gets to 2 => 6, it overwrites the previous value at index 2. This is why 24 is missing from your first array.
If multiple elements in the array declaration use the same key, only the last one will be used as all others are overwritten.
Here's a breakdown
$TestArray1 = [1 => 6]; // Array( [1] => 6 )
// no index, so use last numeric + 1
$TestArray1[] = 24; // Array( [1] => 6, [2] => 24 )
$TestArray1[2] = 6; // Array( [1] => 6, [2] => 6 )
When you manually add numeric indexes that are lower than previous ones (ie $TestArray2), they will be added as provided but their position will be later.
This is because PHP arrays are really maps that just pretend to be indexed arrays sometimes, depending on what's in them.
References are from the PHP manual page for Arrays
I have a column pack_size in a table called product_master_test. The problem that I am facing is that the pack_size is in mixed formats, there is no uniformity to it.
For example:
4 x 2kg (pack size should be 4)
48-43GM (pack size should be 48)
12 x 1BTL (pack size should be 12)
1 x 24EA (pack size should be 24)
I've been thinking about different approaches, but I can't think of anything that would work without having a lot of IF statements in the query/PHP code. Is there a solution that I am missing?
I do have the file in Excel, if there is an easier way to process it using PHP.
I am not including any code, as I'm not entirely sure where to start with this problem.
Using a regex to split the pack size could at least give you the various components which you can then (possibly) infer more from...
$packs = ["4 x 2kg","48-43GM","12 x 1BTL","1 x 24EA", "12 X 1 EA"];
foreach ( $packs as $size ) {
if ( preg_match("/(\d*)(?:\s+)?[xX-](?:\s+)?(\d+)(?:\s+)?(\w*)/", $size, $match) == 1 ) {
print_r($match);
}
else {
echo "cannot determine - ".$size.PHP_EOL;
}
}
(regex can probably be optimised, not my area of expertise). It basically splits it to be a number, some space with either a x or a - and then another number followed by the units (some text). The above with the test cases gives...
Array
(
[0] => 4 x 2kg
[1] => 4
[2] => 2
[3] => kg
)
Array
(
[0] => 48-43GM
[1] => 48
[2] => 43
[3] => GM
)
Array
(
[0] => 12 x 1BTL
[1] => 12
[2] => 1
[3] => BTL
)
Array
(
[0] => 1 x 24EA
[1] => 1
[2] => 24
[3] => EA
)
Array
(
[0] => 12 X 1 EA
[1] => 12
[2] => 1
[3] => EA
)
With the else part it should also give you the ones it cannot determine and perhaps allow you to change it accordingly.
You could present an associative array of all the strings from the table as keys corresponding with correct pack_size you desire.
$packsize = ["4 x 2kg" => 4, "48-43GM" => 48, "12 x 1BTL" => 12, "1 x 24EA" => 24]; //add all pack_sizes here
echo $packsize["4 x 2kg"]; // Output: 4
Now you could get the acutal pack size via the key of associative array. It could save some time you would spend making if/else conditions or switching the input. I'm not sure if there is something wrong with this approach, so correct me if so.
I have a grid of img squares that can be dragged into any order using the sortable library. Each img is a visual representation of a result from a mySQL db query that selects any image that shares an 'imageparent' identifier. The order they're presented in the grid is taken from the 'imageorder' column in the database and starts at 0 and works in sequence up to the nth number of images returned.
The purpose of dragging the img grid is to be able to change the 'imageorder' index. On completion of the drag, the sortable library POSTS an 'imageorder' var by ajax to service.php and is received correctly. So rather than the original 0,1,2,3,4,5,6,7 order of the original, it sends a string like 2,1,0,3,4,5,7,6. Not too hard to grasp. After I switch the order the orderList var sent to service.php is always correct, but the array I end up sending to the db and setting as my session var becomes a little garbled in order after the second or third drag and I'm not quite sure why.
Code Examples and Comments
$_SESSION['selectedCsImages'] Array structure:
[0] => Array
(
[imagename] => "Title"
[imageorder] => 0
[imageid] => 43
)
[1] => Array
(
[imagename] => "Title"
[imageorder] => 1
[imageid] => 21
)
[2] => Array
(
[imagename] => "Title"
[imageorder] => 2
[imageid] => 3
)
etc...
Services.php extract:
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Turn the orderList posted into an array
$removeChars = array('"','[',']');
$orderList = str_replace($removeChars, "", $_POST['order']); // POST received fine.
$listArray = explode(",",$orderList);
// Retrieve the session array
$sorting = $_SESSION['selectedCsImages'];
/* My logic is that I compare the $sorting array to $listArray and reorder $sorting by 'imageorder' to match $listarray */
usort($sorting, function($a, $b) use ($listArray) {
return array_search($a['imageorder'], $listArray) - array_search($b['imageorder'], $listArray);
});
/* I now have a $sorting array that (sometimes, hence the problem) matches the order that the images had just been dragged into by the user. Typically, as I mentioned above, it's correct after the first drag, but not always after the second or third where it creates a new order that I can't see a pattern or logic in. */
/* Had there not been errors with the usort function, I (would) have a $sorting array in the order I want but with imageorder values referring to pre-sorting. I iterate through the array and set each key to 0, 1, 2, etc. so that I have an array in the correct order and with each imageorder correctly stating its place.*/
$i = 0;
foreach ($sorting as $key => $value) {
$sorting[$key]['imageorder'] = $i;
$i++;
}
/* The information is attempted to be sent to the db and, on success I update the session var */
// Database code (runs succesfully and updates the db as per the image orders found in the $sorting array)
$_SESSION['selectedCsImages'] = $sorting;
Debugging:
From debugging, it appears that something happens with the usort function when I call this page from ajax for the second or third time. Everything after this follows through fine and processes the correct or incorrect order as per expectations. The orderList var posted by sortable is correct each time. I'd provide a sample of the $sorted var after usort each time but it's as simple to describe it as the above array example in an order I didn't specify after dragging and I can't see a pattern in the seemingly random order it outputs.
From researching, I had thought that it was an issue with session vars being retained until the page is refreshed but it appears that the ajax call to services.php should refresh the $_SESSION['selectedCsImages'] var. I had also read that, perhaps, I was unknowingly using referenced array values and - as I source from a session var to a new array and, ultimately, save back to this session var from this array - I may have created some messy referencing feedback. However, I tried using $sorted = (array)clone(object)$_SESSION['selectedCsImages']; before attempting usort and the results didn't change.
PHP error logs are showing nothing.
Updates:
Per the suggestion of #Ayaou, I've checked the output of $listArray and am getting some unexpected results. I'd wrongly assumed that as the posted $orderList was correct, that the exploded array would not be a culprit.
Here's the output of print_r($listArray) after completing the following order swaps of 16 img elements: 1st with 2nd, 2nd last with last,6th with 7th:
1st and 2nd:
(
[0] => 1
[1] => 0
[2] => 2
[3] => 3
[4] => 4
[5] => 5
[6] => 6
[7] => 7
[8] => 8
[9] => 9
[10] => 10
[11] => 11
[12] => 12
[13] => 13
[14] => 14
[15] => 15
)
last and 2nd last:
(
[0] => 1
[1] => 0
[2] => 2
[3] => 3
[4] => 4
[5] => 5
[6] => 6
[7] => 7
[8] => 8
[9] => 9
[10] => 10
[11] => 11
[12] => 12
[13] => 13
[14] => 15
[15] => 14
)
6th with 7th:
(
[0] => 1
[1] => 0
[2] => 2
[3] => 3
[4] => 4
[5] => 6
[6] => 5
[7] => 7
[8] => 8
[9] => 9
[10] => 10
[11] => 11
[12] => 12
[13] => 13
[14] => 15
[15] => 14
)
I was progressing with the idea that $listArray would show a sequential 0,1,2,3,etc. each time with only the two swapped items showing order changes. As it's not, I'll look back again at $orderList and check if my sortable library is updating the orders it's obtaining correctly from the updated session var. Older order swaps are being retained somewhere along the chain where they shouldn't.
The solution is on your sortable form (on the front end), so instead of sending the imageorder on your 'order' post data, send the imageid index.
Then change your sort callback like this
//Use imageid index instead of imageorder
usort($sorting, function($a, $b) use ($listArray) {
return array_search($a['imageid'], $listArray) - array_search($b['imageid'], $listArray);
});
I'm running a stock update on our company website, and the query is as follows;
$count = count($stock);
for($i = 0; $i < $count; $i++)
{
if(strtolower($stock[$i]['stockloc']) == 'retail')
{
if($stock[$i]['avail'] < 0)
{
$stock[$i]['avail'] = 0;
}
mysql_query("UPDATE `xcart_products`
SET `avail` = ".$stock[$i]['avail']."
WHERE `productid` IN
(SELECT `productid`
FROM `xcart_extra_field_values`
WHERE `productid` = ".$stock[$i]['productid']."
AND `fieldid` = 5
AND LOWER(value) = 'retail')");
}
}
Eessentially it runs in a for loop and iterates through thousands of products, updating stock levels. It runs fine for about 4 minutes, then things start to lag on our website, mysql seems to become backlogged with other website db queries that it can't run (they all appear locked), which then leads to the inevitable;
Internal Server Error The server encountered an internal error or
misconfiguration and was unable to complete your request.
Please contact the server administrator to inform of the time the
error occurred and of anything you might have done that may have
caused the error.
More information about this error may be available in the server error
log.
It doesn't seem to be a timeout I've upped the limit in PHP.ini to no avail. The query isn't particularly complex, however it does run several thousand times. Maybe as many as 5000+. I'm not sure what the problem is, as we ran a similar update system on our old website, and although it wasn't one I designed, it didn't have trouble uploading this many stock quantity changes.
I'll be happy to expand my question if further information is needed, but honestly, I'm at a bit of a loss even how to trace the problem.
The $stock array is an associative array which has the following format;
Array
(
[0] => Array
(
[avail] => 55
[productid] => 17761
[sku] => AER-TOUCH-2100
[stockloc] => retail
)
[1] => Array
(
[avail] => 166
[productid] => 22653
[sku] => CAS-AER-112
[stockloc] => retail
)
[2] => Array
(
[avail] => 272
[productid] => 22952
[sku] => CAS-ANT-001
[stockloc] => retail
)
[3] => Array
(
[avail] => 5
[productid] => 22956
[sku] => CAS-ANT-005
[stockloc] => retail
)
[4] => Array
(
[avail] => 12
[productid] => 22958
[sku] => CAS-ANT-007
[stockloc] => retail
I have tried running a delay every x iterations of the loop, but I'm still getting the 500 internal server error message. Clearly I need a better solution to updating my table.
A webservice returns a timestamp field in base64Binary format. It looks like this in SOAP response:
<a:TimeStamp>AAAAAAMpI9Q=</a:TimeStamp>
PHP __soapCall, however, b64_decode()s that and I get a binary string looking like ')#▒'. How do I get actual timestamp out of this? I tried to unpack('L') it but it gives me Array([1] => 0) as a result. Is there really zero i.e. 1970-01-01 or have I missed something?
This test program:
$b = "AAAAAAMpI9Q=";
$ts = base64_decode($b);
print_r(array_map("ord", str_split($ts)));
outputs:
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 3
[5] => 41
[6] => 35
[7] => 212
)
showing that the base64-encoded string gives you an 8-character string when unpacked. So presumably it represents a 64-bit integer, which might be signed or unsigned, and no, it isn't zero.
Given the values above it looks like the value is 53027796 - is that what you're expecting?