I am trying to create Presigned URLs for users to access content from an S3 bucket.
The below code was working fine and all of a sudden I am getting the below error when opening any pre-signed URL that is created.
public function getPresignedUri($p)
{
$s3 = new S3Client([
'region' => getenv('S3_REGION'),
'version' => 'latest',
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => getenv('S3_BUCKET'),
'Key' => 'casts/'. $p['file']
]);
$request = $s3->createPresignedRequest($cmd, '+1 hour');
return (string) $request->getUri();
}
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>ASIA3DM6Y5GJC4FYJAFC</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
20180821T072223Z
20180821/ap-southeast-2/s3/aws4_request
fc4f1139d3b146ae027bd0bfc0b3d6dacda81d711b062e0d93a65d04a61aa268</StringToSign><SignatureProvided>5f5d3ae9ef3d9cdfc0d039c39302c584dcfc93f5a94a0f1770bf6781d6958198</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 31 38 30 38 32 31 54 30 37 32 32 32 33 5a 0a 32 30 31 38 30 38 32 31 2f 61 70 2d 73 6f 75 74 68 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 66 63 34 66 31 31 33 39 64 33 62 31 34 36 61 65 30 32 37 62 64 30 62 66 63 30 62 33 64 36 64 61 63 64 61 38 31 64 37 31 31 62 30 36 32 65 30 64 39 33 61 36 35 64 30 34 61 36 31 61 61 32 36 38</StringToSignBytes><CanonicalRequest>GET
/casts/5B735D22BCB17.mp4
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3DM6Y5GJC4FYJAFC%2F20180821%2Fap-southeast-2%2Fs3%2Faws4_request&X-Amz-Date=20180821T072223Z&X-Amz-Expires=3600&X-Amz-Security-Token=FQoGZXIvYXdzEPn%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDEMaw7h8OwK6f6QN0SLBA4%2B9LzXs7OMNjW7HDqr1jhuK%2FshbOMDBoF00GHqTWUuJWXQuL4ptYpvWRjwpris0USWPMTx0O3WeKacvtw6oN2M1KRoUe3IcNOpFwaixKw8%2Fo5FKXK%2BSCo%2F7U%2B76V4aEFuuWEZkC5qhm9R7ChB7vDNTlmYXx2GOzL2uZYV8dZrAnrUfU5qWpyI4IQb8DvPnpDWB0OgA2SRvuGzkwkVLEtMmHS2SMU32gwX2Oy6YnMswWZeqVQ%2FovfWbxd5AA4O%2BFNQfcNM5l4jsuR2zV8FiKZ3jQRLgfQx5uvydv6FFzb90SDbvUjZd0aAsR1Mre%2FnoQodezAm0xoA5618%2FWd%2BIh3jouN2RflRM3II8UXCWzFFq2NL%2FxweJu2mYXfKNpTkqOEls5dFMo2OWQa3IGXJqT3EZEZKXcQ3z%2F2aOP%2Fyw%2F2GtPdQrdJziwN4lTXyl6%2FGZYd968yjlU6pIk6vB0NVq9q3wKjBiwlsfGTlaJnFJH7DD%2FIY4U6fYOmvAcGnoozAbIcqDZpDPNrvZX75tzSatHHLyQoF56STZPhWK7cCWEo2JWAzg6NE4xBmypFG%2Bkxtv0QtrcUNYD35FvFGbjheUhMnyOKOTz7tsF&X-Amz-SignedHeaders=host
host:app-assets-dev-ap-southeast-2-cmpny.s3.ap-southeast-2.amazonaws.com
host
UNSIGNED-PAYLOAD</CanonicalRequest>
AWS SDK verison aws/aws-sdk-php (3.65.0)
One small difference I can see in the URL is that when it was working it had
X-Amz-SignedHeaders=host
and now it has
X-Amz-SignedHeaders=host%3Bx-amz-security-token
Although not sure what could be causing that extra string??
EDIT 1:
I identified that this issue was due to the SDK version 3.65 ... when I rolled back to 3.31 there was no issue.
However I am not marking this as resolved as I would like to know why a small version change like this made such a big difference and error?
I can see there is major differences in the src/Signature/SignatureS4.php file specifically:
$parsed['query']['X-Amz-SignedHeaders'] = 'host'; (V3.31)
and
$parsed['query']['X-Amz-SignedHeaders'] = implode(';', $this->getPresignHeaders($parsed['headers'])); (V3.65)
however that line alone didn't fix - replacing the whole file did fix the error.
I logged this issue on Github - https://github.com/aws/aws-sdk-php/issues/1609
And it was tested, confirmed and resolved very quickly - https://github.com/aws/aws-sdk-php/pull/1610
This is resolved.
Related
I have gps tracker which is sending data to my server over tcp and my server has been coded in PHP. the device is sending data in every 5 seconds. The issue is that the first response comes fine as soon as socket established connection with the client but then subsequent responses comes in weird characters and no idea of how to interpret them.
Server.php code
SocketServer.class.php
<?php
require_once("SocketServer.class.php");
$server = new SocketServer("172.xx.xx.xxx",8000);
$server->max_clients = 10;
$server->hook("CONNECT","handle_connect"); // Run handle_connect every time someone connects
$server->hook("INPUT","handle_input");
$server->infinite_loop();
function handle_connect(&$server,&$client,$input) {
SocketServer::socket_write_smart($client->socket,"String? ","");
}
function handle_input(&$server,&$client,$input) {
$trim = trim($input);
if(strtolower($trim) == "quit")
{
SocketServer::socket_write_smart($client->socket,"Oh... Goodbye...");
$server->disconnect($client->server_clients_index); // Disconnect this client.
return;
}
$output = strrev($trim);
SocketServer::socket_write_smart($client->socket,$output);
SocketServer::socket_write_smart($client->socket,"String? ","");
}
Output:
Triggering Hook 'handle_connect' for 'CONNECT'
*HQ,9176952312,V6,115939,V,3127.2665,N,07307.6974,E,0.00,126.00,100822,FFF7FBFF,410,1,325,14688,8992300001243970980F,#
Triggering Hook 'handle_input' for 'INPUT'
0#119.160.59.240 --> $?v?#uY"1'&e0v?
&????ʧ?E9`,
Triggering Hook 'handle_input' for 'INPUT'
0#119.160.59.240 --> $?v?#uY"1'&e0v?
&????ʧ?E9`,
hex dump if data comes correctly
<Buffer 2a 48 51 2c 39 31 37 36 39 35 32 33 37 35 2c 56 36 2c 31 35 35 37 32 32 2c 56 2c 33 31 32 37 2e 33 30 33 34 2c 4e 2c 37 33 30 37 2e 36 36 33 36 2c 45 ... 65 more bytes>
Hex dump incorrect response
24 91 76 95 23 75 15 57 24 10 08 22 31 27 30 34 04 07 30 76 63 0c 00 00 00 ff f7 fb ff 00 01 c7 f0 00 00 00 00 01 9a 01 01 45 7f 2d 00
I want to upload files to aws s3 through pre signed url. I used this github project https://github.com/JoernBerkefeld/s3SignedUpload but It shows error while uploading.
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<AWSAccessKeyId>AKIAI2DLTCOXU5255TMQ</AWSAccessKeyId>
<StringToSign>
AWS4-HMAC-SHA256 20160810T174345Z 20160810/ap-south-1/s3/aws4_request a63f9ead869c1fb4d06fa1169458b87978d86fd44348144636c2e0cb2c10cdf5
</StringToSign>
<SignatureProvided>
ae7bd98265316de85f3c55e539ca9bdb5934620428a7924b9404a9403dd9d4d2
</SignatureProvided>
<StringToSignBytes>
41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 31 36 30 38 31 30 54 31 37 34 33 34 35 5a 0a 32 30 31 36 30 38 31 30 2f 61 70 2d 73 6f 75 74 68 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 61 36 33 66 39 65 61 64 38 36 39 63 31 66 62 34 64 30 36 66 61 31 31 36 39 34 35 38 62 38 37 39 37 38 64 38 36 66 64 34 34 33 34 38 31 34 34 36 33 36 63 32 65 30 63 62 32 63 31 30 63 64 66 35
</StringToSignBytes>
<CanonicalRequest>
GET /logo.jpg Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAI2DLTCOXU5255TMQ%2F20160810%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20160810T174345Z&X-Amz-Expires=1800&X-Amz-SignedHeaders=Host host:indiastreetz.s3.ap-south-1.amazonaws.com host UNSIGNED-PAYLOAD
</CanonicalRequest>
<CanonicalRequestBytes>
47 45 54 0a 2f 6c 6f 67 6f 2e 6a 70 67 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3d 69 6d 61 67 65 25 32 46 6a 70 65 67 26 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 49 32 44 4c 54 43 4f 58 55 35 32 35 35 54 4d 51 25 32 46 32 30 31 36 30 38 31 30 25 32 46 61 70 2d 73 6f 75 74 68 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 31 36 30 38 31 30 54 31 37 34 33 34 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 31 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 48 6f 73 74 0a 68 6f 73 74 3a 69 6e 64 69 61 73 74 72 65 65 74 7a 2e 73 33 2e 61 70 2d 73 6f 75 74 68 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44
</CanonicalRequestBytes>
<RequestId>78A21E02C733E895</RequestId>
<HostId>
rBd5ZQBuMyjouFZbSavDcFX4n4fEuqSj8aZuCQyw9rChSSpZkBFZjd8CXqog2OWPMX96OSIuFmo=
</HostId>
</Error>
Answers I found at stackoverflow doesn't fix my solution.Please tell me how to fix or where to find the error.
public function getSignedUrl($filename, $mime) {
if(!$filename) {
return $this->error('filename missing');
}
if(!$mime) {
return $this->error('mime-type missing');
}
$final_filename = $this->get_file_name($filename);
try {
$signedUrl = $this->client->getCommand('PutObject', array(
'Bucket' => $this->bucket,
'Key' => $this->folder.$final_filename,
'ContentType' => $mime,
'Body' => '',
'ContentMD5' => false
))->createPresignedUrl('+30 minutes');
} catch (S3Exception $e) {
echo $e->getMessage() . "\n";
}
$signedUrl .= '&Content-Type='.urlencode($mime);
return array('url'=>$signedUrl);
}
I used PHP inbuilt hash functions to pre-sign a upload and then upload the file with POST.
When a user clicks an upload button, it will get a pre-signed data from presign_url and then begin upload to aws.
$.ajax({
url: 'presign_url.php',
type: 'post',
dataType: 'json',
success: function (data) {
send_img(png,image_size,data); /* append this data with your post data. I pass the data to my image uploading function.data contains presigned data(image name,file size,format and other amazon credentials )*/
},
data: {}
});
//presign_url.php
<?php
$access_key = "XXXXXXXXXXXX"; //Access Key
$secret_key = "xyzxyzxyzxyz"; //Secret Key
$my_bucket = "bucketname"; //bucket name
$region = "your region"; //bucket region ex:-ap-south-1
$success_redirect = "http://localhost/info";// your redirect url
$_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; //URL to which the client is redirected upon success (currently self)
$allowd_file_size = "1048579"; //1 MB allowed Size
//dates
$short_date = gmdate('Ymd'); //short date
$iso_date = gmdate("Ymd\THis\Z"); //iso format date
$expiration_date = gmdate('Y-m-d\TG:i:s\Z', strtotime('+1 hours')); //policy expiration 1 hour from now
$filename=uniqid().".jpg"; // file name to be which file is saved.
//POST Policy required in order to control what is allowed in the request
//For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
$policy = utf8_encode(json_encode(array(
'expiration' => $expiration_date,
'conditions' => array(
array('acl' => 'public-read'),
array('bucket' => $my_bucket),
array('success_action_redirect' => $success_redirect),
array('starts-with', '$key','user/'),
array('content-length-range', '1', $allowd_file_size),
array('x-amz-credential' => $access_key.'/'.$short_date.'/'.$region.'/s3/aws4_request'),
array('x-amz-algorithm' => 'AWS4-HMAC-SHA256'),
array('X-amz-date' => $iso_date)
))));
//Signature calculation (AWS Signature Version 4)
//For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
$kDate = hash_hmac('sha256', $short_date, 'AWS4' . $secret_key, true);
$kRegion = hash_hmac('sha256', $region, $kDate, true);
$kService = hash_hmac('sha256', "s3", $kRegion, true);
$kSigning = hash_hmac('sha256', "aws4_request", $kService, true);
$signature = hash_hmac('sha256', base64_encode($policy), $kSigning);
$arr=array();
$arr["u"]="http://$my_bucket.s3.amazonaws.com/";
$arr["k"]=$filename;
$arr["a"]="public-read";
$arr["ac"]="$access_key/$short_date/$region/s3/aws4_request";
$arr["aa"]="AWS4-HMAC-SHA256";
$arr["ad"]=$iso_date;
$arr["p"]=base64_encode($policy);
$arr["as"]="$signature";
$arr["sar"]="$success_redirect";
$arr["us"]="user/";
echo json_encode($arr);
?>
I use the following test program to retrieve a website's content:
<?php
function getData( $url, $output ) {
// set the path for CURL
if (file_exists( '/var/lib'))
$curl = 'curl';
else
$curl = 'curl.exe';
$curl .= ' --trace trace.txt --header "User-Agent: Some-Agent/1.0" ';
echo "\nreading $url...\n";
$buffer = shell_exec( "$curl -i \"$url\"" );
// if this is a 301 redirection URL, follow it one step
if ((preg_match( '~^HTTP.+? 301 ~', $buffer )) and preg_match( '~Location: (.+)~', $buffer, $location )) {
$cmd = "$curl -i \"$location[1]\"";
echo "$cmd\n";
$buffer = shell_exec( $cmd );
}
file_put_contents( $output, $buffer );
}
// test with a URL that will be redirected:
getData( "http://www.onvista.de/aktien/fundamental/EISEN-UND-HUETTENWERKE-AG-Aktie-DE0006055007", "DE0006055007-AG.html" );
On my windows machine, this code runs fine. On a linux machine it returns a 500 internal server error.
This is the start of the trace file trace.txt:
== Info: About to connect() to www.onvista.de port 80 (#0)<br>
== Info: Trying 217.11.205.10... == Info: connected<br>
== Info: Connected to www.onvista.de (217.11.205.10) port 80 (#0)<br>
=> Send header, 130 bytes (0x82)<br>
0000: 47 45 54 20 2f 61 6b 74 69 65 6e 2f 66 75 6e 64 GET /aktien/fund<br>
0010: 61 6d 65 6e 74 61 6c 2f 31 53 54 2d 52 45 44 2d amental/1ST-RED-<br>
0020: 41 47 2d 41 6b 74 69 65 2d 44 45 30 30 30 36 30 AG-Aktie-DE00060<br>
0030: 35 35 30 30 37 0d 20 48 54 54 50 2f 31 2e 31 0d 55007. HTTP/1.1.<br>
0040: 0a 48 6f 73 74 3a 20 77 77 77 2e 6f 6e 76 69 73 .Host: www.onvis<br>
0050: 74 61 2e 64 65 0d 0a 41 63 63 65 70 74 3a 20 2a ta.de..Accept: *<br>
0060: 2f 2a 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 /*..User-Agent: <br>
0070: 53 6f 6d 65 2d 41 67 65 6e 74 2f 31 2e 30 0d 0a Some-Agent/1.0..<br>
0080: 0d 0a ..<br>
<= Recv header, 36 bytes (0x24)<br>
0000: 48 54 54 50 2f 31 2e 31 20 35 30 30 20 49 6e 74 HTTP/1.1 500 Int<br>
0010: 65 72 6e 61 6c 20 53 65 72 76 65 72 20 45 72 72 ernal Server Err<br>
0020: 6f 72 0d 0a <br>
The only difference between the windows trace and this one is a CR character after the filename (ending in DE0006055007. How did get there and how can I suppress it? (And no, I don't want to use the PHP cURL module which leads to other problems.)
The http headers you get end with \r\n, as should. It seems curl on linux converts them to \n if it detects the output is tty, but try to redirect to a file and you'll see the \rs in there.
. in preg_match matches also \r character, so it becomes part of $location[1]. Simple solution is to trim it.
This doesn't happen on windows only because you can execute curl -i "http://google.com. The quotation is ended by the shell automaticaly after newline.
And you should really use escapeshellarg.
I've read and tried every answer here, yet they seem to only apply to strings in non-binary formats. I'm trying to compare differences in binary files and return those in format such as this:
[file1]
-0001010: ac 0f 00 00 01 00 00 00 48 65 6c 6c 6f 2c 20 77 ........Hello, w
[file2]
+0001010: ac 0f 00 00 01 00 00 00 48 75 6c 6c 6f 2c 20 77 ........Hullo, w
xdiff works fine for creating bdiff patches and patching file - I'm looking too illustrate the differences.
$one = 'one'; // original file
$two = 'two'; // updated file
$pat = 'dif'; // bdiff patch
$new = 'new'; // new destfile
xdiff_file_diff_binary($one, $two, $pat);
xdiff_file_patch_binary($one, $pat, $new);
$diff = xdiff_file_diff($one, $two, 1);
if (is_file($diff)) {
echo "Differences:\n"; // result = 1
echo $diff;
}
Maybe xdiff isn't the right extension to be using for this? I'm not sure.
Sound like a big pain in the butt to do in PHP, might I suggest the following bash one-liner?
diff <(hexdump -C file1) <(hexdump -C file2)
Output:
10c10
< 00000090 35 35 61 34 32 62 62 31 30 33 31 62 38 38 39 34 |55a42bb1031b8894|
---
> 00000090 35 35 61 34 32 62 62 31 30 33 31 61 38 38 39 34 |55a42bb1031a8894|
And you can always mess with the options for diff and hexdump.
I have a file that looks like this (yes the line breaks are right):
39 9
30 30 30 31 34 30 30 32 33 32 36 30 31 38 0D 0A 00014002326018..
39 30 30 30 31 34 30 30 32 33 32 36 30 35 34 0D 900014002326054.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 30 39 31 0D 900014002326091.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 31 36 33 0D 900014002326163.
0A .
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 32 30 30 0D 0A 26200..
39 9
30 30 30 31 34 30 30 32 33 32 36 32 30 30 0D 0A 00014002326200..
39 30 30 30 31 34 30 30 32 33 32 36 31 32 32 0D 900014002326122.
0A .
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 35 34 0D 0A 26154..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 33 31 0D 0A 26131..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 30 34 0D 0A 26104..
39 30 30 30 31 34 30 30 32 33 32 36 30 39 30 0D 900014002326090.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 31 39 37 0D 900014002326197.
0A .
39 9
30 30 30 31 34 30 30 32 33 32 36 32 30 38 0D 0A 00014002326208..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 31 35 0D 0A 26115..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 36 34 0D 0A 26164..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 30 31 36 0D 0A 39 30 30 30 31 34 30 30 32 26016..900014002
33 3
32 36 32 34 36 0D 0A 26246..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 32 34 36 0D 0A 26246..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 30 37 39 0D 0A 26079..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 32 30 0D 0A 26120..
39 9
30 30 30 31 34 30 30 32 33 32 36 32 32 38 0D 0A 00014002326228..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 38 36 0D 0A 26186..
I have this code that grabs the EID tags (the numbers that start with 9000) but I can't figure out how to get it to do multiple lines.
$data = file_get_contents('tags.txt');
$pattern = "/(\d{15})/i";
preg_match_all($pattern, $data, $tags);
$count = 0;
foreach ( $tags[0] as $tag ){
echo $tag . '<br />';
$count++;
}
echo "<br />" . $count . " total head scanned";
For example the first and second line should return 900014002326018 instead of ignoring the first and second line
I am not good with regular expressions, so if you could explain so I learn and stop having to have someone help me with simple regex, that would be awesome.
EDIT: The whole number is 15 digits starting with 9000
You can do this:
$result = preg_replace('~\R?(?:[0-9A-F]{2}\h+)+~', '', $data);
$result = explode('..', rtrim($result, '.'));
pattern details:
\R? # optional newline character
(?: # open a non-capturing group
[0-9A-F]{2} # two hexadecimal characters
\h+ # horizontal white characters (spaces or tabs)
)+ # repeat the non-capturing group one or more times
After this replacement the only content you must remove are the two dots. After removing the trailing dots, you can use these to explode the string to an array.
An other way
Since you know that there is always 48 characters before the part of integers (and dots), you can use this pattern too:
$result = preg_replace('~(?:^|\R).{48}~', '', $data);
An other way without regex
The idea is to read the file line by line and, since the length before the content is always the same (i.e. 16*3 characters -> 48 characters), extract the substring with the integer and concatenate it into the $data temporary variable.
ini_set("auto_detect_line_endings", true);
$data = '';
$handle = #fopen("tags.txt", "r");
if ($handle) {
while (($buffer = fgets($handle, 128)) !== false) {
$data .= substr($buffer, 48, -1);
}
if (!feof($handle)) {
echo "Error: fgets() has failed\n";
}
fclose($handle);
} else {
echo "Error opening the file\n";
}
$result = explode ('..', rtrim($data, '.'));
Note: if the file has a windows format (with the end of line \r\n) you must change the third parameter of the substr() function to -2. If you are interested by how to detect newlines type, you can take a look at this post.
I don't think it's even possible to do this with a single regex, but your code will be far more legible and maintainable if you approach this one step at a time.
This works, and it shouldn't be too hard to figure out how it works:
$eid_tag_src = <<<END_EID_TAGS
39 9
30 30 30 31 34 30 30 32 33 32 36 30 31 38 0D 0A 00014002326018..
39 30 30 30 31 34 30 30 32 33 32 36 30 35 34 0D 900014002326054.
:
etc.
:
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 38 36 0D 0A 26186..
END_EID_TAGS;
/* Remove hex data from first 48 characters of each line */
$eid_tag_src = preg_replace('/^.{48}/m','',$eid_tag_src);
/* Remove all white space */
$eid_tag_src = preg_replace('/\s+/','',$eid_tag_src);
/* Replace dots (CRLF) with spaces */
$eid_tag_src = str_replace('..',' ',$eid_tag_src);
/* Convert to array of EID tags */
$eid_tags = explode(' ',trim($eid_tag_src));
print_r($eid_tags);
Here's the output:
Array
(
[0] => 900014002326018
[1] => 900014002326054
[2] => 900014002326091
[3] => 900014002326163
[4] => 900014002326200
[5] => 900014002326200
[6] => 900014002326122
[7] => 900014002326154
[8] => 900014002326131
[9] => 900014002326104
[10] => 900014002326090
[11] => 900014002326197
[12] => 900014002326208
[13] => 900014002326115
[14] => 900014002326164
[15] => 900014002326016
[16] => 900014002326246
[17] => 900014002326246
[18] => 900014002326079
[19] => 900014002326120
[20] => 900014002326228
[21] => 900014002326186
)
Here's an approach using effective grabbing (without replacing):
RegEx: /(?:^.{48}|\.)([0-9]+\.?)/m - explained demo
Which means (in plain english): start grabbing digits followed by an optional dot IF from the start of the line there are 48 characters in front of them OR a dot (special case).
And your code could look like this:
$pattern = '/(?:^.{48}|\.)([0-9]+\.?)/m';
preg_match_all($pattern, $data, $tags);
//join all the bits belonging to the number
$data=implode("", $tags[1]);
//count the dots to have a correct count of the numbers grabbed
//since each number was grabbed with an ending dot initially
$count=substr_count($data, ".");
//replace the dots with a html <br> tag (avoiding a split and a foreach loop)
$tags=str_replace('.', "<br>", $data);
print $tags . "<br>" . $count . " total scanned";
See the code live at http://3v4l.org/Z4EhI