How to build concatenated sms pdu? Getting junk chars - php

I'm trying to build some PHP code to send SMS through telnet to a SIM server, but I'm having trouble in sending concatenated messages.
I've read some things about using padding bits to make the coded message septets into octets, but I don't fully understand how it works.
I have a class that receives the phone number, the message (already split in 153chars maximum), the total number of SMSs and the order number of the present part of text.
It works as long as I have that '20' added before the $hexmessage. But I get a junk char in the beginning of the first part (before the 1st letter of my msg), and the same junk char replacing the first letter of the second part! (using '20' so it would show a blank space, but it shows a triangle)
I can't understand why, or what I'd had to change for it to work properly.
I hope that someone can help me understand what am I doing wrong.
Here's what I've got so far:
<?php
// Generate PDU string
public function generatePDUm($receiverNumber,$message, $sms_count, $msg_nr) {
//Append filler digit if number is national
if( strlen($receiverNumber)==9){
$nacional=1;
$receiverNumber = $receiverNumber."F";
$network = substr($receiverNumber, 0, 2); //NETWORK code, used to decide the SIM Card to be used
//Check for international flags and set the number type accordingly
}else{
$nacional=0;
if(substr($receiverNumber, 0, 1)=='+'){
$network = substr($receiverNumber, 4, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 1, 12); //remove international indicator
}
else if(substr($receiverNumber, 0, 2)== '00'){
$network = substr($receiverNumber, 5, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 2, 12); //remove international indicator
}
}
/* Flag the network to be used */
switch ($network){
case "92":
$network="TMN";
break;
case "96":
$network="TMN";
break;
case "91":
$network="VODAFONE";
break;
case "93":
$network="OPTIMUS";
break;
}
// Receiver number must be 10 characters long ('national nr' + filler digit) or less than 13 ('351'+'national nr'). (Portugal)
if( strlen($receiverNumber) < 10 || strlen($receiverNumber) > 12) {
// Error, not 10 or over 12 numbers long (Code 1)
$this->setErrorCode(1);
return false;
}
// Message must be 2 characters long at least
if( strlen($message) < 2 ) {
// Error, message too short (Code 2)
$this->setErrorCode(2);
return false;
}
// Message can't be longer than 153 characters. 3SMS.
if( strlen($message) > 153 ) {
// Error, message too long (Code 3)
$this->setErrorCode(3);
return false;
}
// Length of servicecenter number (00 = automatically fixed by phone)
$serviceCenterNumberLength = '00';
// SMS-? : 04=sms-deliver(recieve), 11=sms-submit, 01 = dont know but it works, 41 = SMS-SUBMIT + UDH bit (for extended/concatenated SMS)
// You can try to change this if your phone does not work with 01 command try to use 11 command
$smsType = '41';
// TP Message Reference: (placeholder), let the phone set the message reference number itself
$messageRef = '00';
// Number length. If national -> 9, if international -> 12
if($nacional==1){
$numberLength = '09';
}else{
$numberLength = '0C';
}
// Type of phone adress: (81=Unknown=10dec, 91=InternationalFormat, 92=National?)
if($nacional==1){
$numberType = '81';
}else{
$numberType = '91';
}
// Get the PDU version of the number
$number = $this->getNumberAsPDU( $receiverNumber );
// TP-PID (Protocol Identifier)
$protocolId = '00';
// TP-DCS (Data coding scheme)
$dataCodingScheme = '00';
// TP-Validity-Period (timestamp), AA=4days expiry, disabled for SonyEricsson support.
// $validityPeriod = 'A0';
// $validityPeriod = 'AA'; // Add this if the PDU command fails
/*user data header information (05 - User Data header info length
* 00 - Information element identifier for a concatenated short message
* 03 - Information element data length
* 00 - Reference number, auto
* 0.$sms_count - total SMS nr
* 0.$msg_nr - current SMS order */
$udhi = '050003000'.$sms_count.'0'.$msg_nr;
// echo 'UDHinfo: '.$udhi."\n";
// Data length of message (in hex format)
$dataLength = $this->strToHexLen($message);
// echo 'DATA LENGHT: '.$dataLength."\n\n";
// Convert message, string > 7bits > 8bits > hex
$hexMessage = $this->bit7tohex( $this->strto7bit( $message ) );
// Create the complete PDU string
$pdu = $serviceCenterNumberLength . $smsType . $messageRef . $numberLength .
$numberType . $number . $protocolId . $dataCodingScheme . $dataLength .
$udhi . '20' .$hexMessage;
/*
* Generate the length of var $pdu (pdu/2 minus 1) as pdu format requests
* The -1 is because we don't count the first two characters '00', needed for this command: 'cmgs=24'
*/
$cmgslen = strlen($pdu)/2-1;
// Build data array to return with required information
$data = array();
$data['pdu'] = $pdu;
$data['cmgslen'] = $cmgslen;
$data['rede'] = $network;
// Return the data array with PDU information
return $data;
}
// Generate PDU formatted cellphone number
private function getNumberAsPDU($number) {
// Length of number divided by 2 handle two characters each time
$length = strlen( $number )/2;
// Set counter to 1 for strlen
$i = 1;
$pduNumber = '';
// Loop to handle every 2 characters of the phone number. 06 12 34 56 78
while ($i <= $length) {
// Get 2 characters of the complete string depending on the number of the current loop.
// Then reverse these 2 characters and put them in var $pduNumber (06 = 60)
$pduNumber .= strrev( substr( $number,$i*2-2,2) );
// Counter + 1
$i++;
}
// Return the generated number
return $pduNumber;
}
/* Function to convert ascii character to 8 bits
* Much more efficient than holding a complete ASCII table
* Thanks to Mattijs F.
*/
private function asc2bin($input, $length=8) {
$bin_out = '';
// Loop through every character in the string
for($charCount=0; $charCount < strlen($input); $charCount++) {
$charAscii = ord($input{$charCount}); // ascii value of character
$charBinary = decbin($charAscii); // decimal to binary
$charBinary = str_pad($charBinary, $length, '0', STR_PAD_LEFT);
$bin_out .= $charBinary;
}
// Return complete generated string
return $bin_out;
}
// String to 7 bits array
private function strto7bit($message) {
$message = trim($message);
$length = strlen( $message );
$i = 1;
$bitArray = array();
// Loop through every character in the string
while ($i <= $length) {
// Convert this character to a 7 bits value and insert it into the array
$bitArray[] = $this->asc2bin( substr( $message ,$i-1,1) ,7);
$i++;
}
// Return array containing 7 bits values
return $bitArray;
}
// Convert 8 bits binary string to hex values (like F2)
private function bit8tohex($bin, $padding=false, $uppercase=true) {
$hex = '';
// Last item for counter (for-loop)
$last = strlen($bin)-1;
// Loop for every item
for($i=0; $i<=$last; $i++) {
$hex += $bin[$last-$i] * pow(2,$i);
}
// Convert from decimal to hexadecimal
$hex = dechex($hex);
// Add a 0 (zero) if there is only 1 value returned, like 'F'
if($padding && strlen($hex) < 2 ) {
$hex = '0'.$hex;
}
// If we want the output returned as UPPERCASE do this
if($uppercase) {
$hex = strtoupper($hex);
}
// Return the hexadecimal value
return $hex;
}
// Convert 7 bits binary to hex, 7 bits > 8 bits > hex
private function bit7tohex($bits) {
$i = 0;
$hexOutput = '';
$running = true;
// For every 7 bits character array item
while($running) {
if(count($bits)==$i+1) {
$running = false;
}
$value = $bits[$i];
if($value=='') {
$i++;
continue;
}
// Convert the 7 bits value to the 8 bits value
// Merge a part of the next array element and a part of the current one
// Default: new value is current value
$new = $value;
if(key_exists(($i+1), $bits)) {
// There is a next array item so make it 8 bits
$neededChar = 8 - strlen($value);
// Get the char;s from the next array item
$charFromNext = substr($bits[$i+1], -$neededChar);
// Remove used bit's from next array item
$bits[$i+1] = substr($bits[$i+1], 0, strlen($bits[$i+1])-$neededChar );
// New value is characters from next value and current value
$new = $charFromNext.$value;
}
if($new!='') {
// Always make 8 bits
$new = str_pad($new, 8, '0', STR_PAD_LEFT);
// The 8 bits to hex conversion
$hexOutput .= $this->bit8tohex($new, true);
}
$i++;
}
// Return the 7bits->8bits->hexadecimal generated value
return $hexOutput;
}
// String to length in Hex, String > StringLength > Hex
private function strToHexLen($message) {
// Length of the string (message)
$length = strlen( $message )+7; //+7 for UDH. the UDH is a total of (number of octets x bit size of octets) 6 x 8 = 48 bits long. Therefore a single bit of padding has to be prepended to the message. The UDH is therefore (bits for UDH / bits per septet) = (48 + 1)/7 = 7 septets in length.
// Hex value of this string length
$hex = dechex($length);
// Length of the hex value
$hexLength = strlen($hex);
// If the hex strng length is lower dan 2
if($hexLength < 2) {
// Add a 0 (zero) before it
$hex = '0'.$hex;
}
// Return the hex value in UPPERCASE characters
return strtoupper($hex);
}
}
?>

As you are already aware, creating concatenated SMS messages requires you to add a UDH before your text message. The UDH becomes part of your payload, thus reducing the number of characters you can send per segment.
As it has become part of your payload, it needs to confirm with your payloads data requirement - which is 7 bit. The UDH however, is 8 bit, which clearly complicates things.
Consider the UDH of the following:
050003000302
05 is the length of the UDH
00 is the IEI
03 is the IEDL (3 more octets)
00 is a reference (this number must be the same in each of your concatenated message UDH's)
03 is the maximum number of messages
02 is the current message number.
This is 6 octets in total - equating to 48 bits. This is all and well, but since the UDH is actually part of your SMS message, what you have to do is add more bits so that the actual message starts on a septet boundary. A septet boundary is every 7 bits, so in this case, we will have to add 1 more bit of data to make the UDH 49 bits, and then we can add our standard GSM-7 encoded characters.
You can read up more about this from Here

These questions are all over the place and no one seems to be able to answer them in way that makes any sense. Zero padding usually just makes things worse. I think easiest way to go around this design flaw in GMS standard is to use 8 bit encoding or 16-bit UCS2 even that it means less characters. In that way you don't need to care about difference in byte boundaries which is the reason creating concatenated SMS is so hard.

Related

Convert string to consistent but random 1 of 10 options

I have many strings. Each string something like:
"i_love_pizza_123"
"whatever_this_is_now_later"
"programming_is_awesome"
"stack_overflow_ftw"
...etc
I need to be able to convert each string to a random number, 1-10. Each time that string gets converted, it should consistently be the same number. A sampling of strings, even with similar text should result in a fairly even spread of values 1-10.
My first thought was to do something like md5($string), then break down a-f,0-9 into ten roughly-equal groups, determine where the first character of the hash falls, and put it in that group. But doing so seems to have issues when converting 16 down to 10 by multiplying by 0.625, but that causes the spread to be uneven.
Thoughts on a good method to consistently convert a string to a random/repeatable number, 1-10? There has to be an easier way.
Here's a quick demo how you can do it.
function getOneToTenHash($str) {
$hash = hash('sha256', $str, true);
$unpacked = unpack("L", $hash); // convert first 4 bytes of hash to 32-bit unsigned int
$val = $unpacked[1];
return ($val % 10) + 1; // get 1 - 10 value
}
for ($i = 0; $i < 100; $i++) {
echo getOneToTenHash('str' . $i) . "\n";
}
How it works:
Basically you get the output of a hash function and downscale it to desired range (1..10 in this case).
In the example above, I used sha256 hash function which returns 32 bytes of arbitrary binary data. Then I extract just first 4 bytes as integer value (unpack()).
At this point I have a 4 bytes integer value (0..4294967295 range). In order to downscale it to 1..10 range I just take the remainder of division by 10 (0..9) and add 1.
It's not the only way to downscale the range but an easy one.
So, the above example consists of 3 steps:
get the hash value
convert the hash value to integer
downscale integer range
A much shorter example with crc32() function which returns integer value right away thus allowing us to omit step 2:
function getOneToTenHash($str) {
$int = crc32($str); // 0..4294967295
return ($int % 10) + 1; // 1..10
}
below maybe what u want
$inStr = "hello world";
$md5Str = md5($inStr);
$len = strlen($md5Str);
$out = 0;
for($i=0; $i<$len; $i++) {
$out = 7*$out + intval($md5Str[$i]); // if you want more random, can and random() here
}
$out = ($out % 10 + 9)%10; // scope= [1,10]

Printing to a dot matrix printer in draft mode - PHP

I have a PHP web application which an option of generating invoices. The Problem is i need to print the invoices using the dot matrix printer in draft mode. I have tries, but it prints like a normal printer with bigger fonts. simply it prints in the format as same in the page.
I need to print like receipts. Do I need to format anything with the PHP?
Well, I think you need to print in raw mode.
I do not claim I have completely solved this issue but here are my tries.
Firstly, there should be a "generic / text only" driver on the system where the dot matrix printer is connected to, probably in port LPT1.
Secondly, you should send ESCP commands to the printer and not use the build in processes of php to print.
You could for example create an array of 135 * 66 characters, where 135 is the usual page width in characters and 66 the usual number of printing lines (on a 24 pin dot matrix printer with a 8-1/2" x 12" paper size). You should be careful to fill the array with characters and not with strings, lie this:
$GRAND_ARRAY = array();
$maxCharactersPerLine = 135;
$maxLines = 66;
//initialize the array with spaces and the last column of \r\n
for($i=0; $i<$maxLines; $i++){
$pos = $i*$maxCharactersPerLine + ($maxCharactersPerLine-1) + $i + 1;
$value = "\r\n";
$this->printChars($GRAND_ARRAY, $pos, $value);
}
//make an array that fits every one character of the page
//plus a column for the "next line" character
$totalCells = $maxCharactersPerLine * $maxLines + $maxLines - 1;
//what you want to print
$value = "name:"; //here is a string with 5 characters
//where you want to print it on the page
//$line = 2
//$column = 5
//consider that array starts at 0 and not 1
//$pos = ($column - 1) * $maxCharactersPerLine + $line + $column - 2;
$pos = 275; //position is 5th character in line 2
//do not just $GRAND_ARRAY[$pos] = $value because you should
//make sure that every array cell has only one character
$this->printChars($GRAND_ARRAY, $pos, $value, 1);
// also included a flag if you want to print only the $limit characters of $newstring
public function printChars(&$mainArray, $start, $newString, $limit=0){
$j = $start;
$charsArray = $this->mb_str_split($newString);
//$len = mb_strlen($newString, "UTF-8"); //can't remember why not this
$len = count($charsArray);
for($i=0; $i<$len; $i++){
if($limit > 0 && $limit < $i){
return;
}else{
$mainArray[$j] = $charsArray[$i];
$j = $j + 1;
}
}
}
//this I found here on s.o. i think.
public function mb_str_split( $string ) {
# Split at all position not after the start: ^
# and not before the end: $
return preg_split('/(?<!^)(?!$)/u', $string );
}
After creating the array I used qz-print plugin (https://code.google.com/p/jzebra/) that includes javascript functions and a java applet in order to send this array to the "generic / text only" dot matrix printer in the client.
The javascript function I use is:
//just before that, I turn the array into string str.
function printESCP2(str, type, quality, pitch, line_spacing, topOffset, commands) {
if (notReady()) { return; }
qz.appendHex("x1Bx40"); //ESC # for reset settings
var j = commands.length;
for(var k=0;k<j;k++){
console.log(':'+commands[k]);
qz.appendHex(commands[k]);
}
if(topOffset){
if(topOffset > 0){
alert("topOffset: "+topOffset);
for(var k=0; k<topOffset; k++){
qz.appendHex("x0D"); //CR for carriage return
qz.appendHex("x0A"); //LF for line feed
}
}
}
if(quality == <?php echo SettingsPrinter::FONT_QUALITY_LQ; ?>){
alert("font quality: LQ");
qz.appendHex("x1Bx78x31"); // ESC x 1 for LQ (31=1)
}else{
alert("font quality: draft");
qz.appendHex("x1Bx78x30"); // ESC x 0 for Draft (30=0)
}
if(line_spacing == <?php echo SettingsPrinter::PRINTER_LINE_SPACING_NARROW; ?>){
//alert("line spacing: narrow (1/8)");
qz.appendHex("x1Bx30"); //ESC 0 for line spacing minimum (1/8)
}else{
//alert("line spacing: normal (1/6)");
qz.appendHex("x1Bx32"); //ESC 2 for line spacing normal (1/6)
}
switch(pitch){
case '<?php echo SettingsPrinter::FONT_PITCH_10; ?>':
alert("pitch: 10");
qz.appendHex("x1Bx50"); //ESC P for pitch 10 cpi
break;
case '<?php echo SettingsPrinter::FONT_PITCH_12; ?>':
alert("pitch: 12");
qz.appendHex("x1Bx4D"); //ESC M for pitch 12 cpi
break;
case '<?php echo SettingsPrinter::FONT_PITCH_15; ?>':
alert("pitch: 15");
qz.appendHex("x1Bx67"); // ESC g for pitch 15 cpi
break;
case '<?php echo SettingsPrinter::FONT_PITCH_17; ?>':
alert("pitch: 17");
qz.appendHex("x1Bx50"); //ESC P for pitch 10 cpi
qz.appendHex("x0F"); // SI for condensed resulting in pitch 17 cpi
break;
case '<?php echo SettingsPrinter::FONT_PITCH_20; ?>':
alert("pitch: 20");
qz.appendHex("x1Bx4D"); //ESC M for pitch 12 cpi
qz.appendHex("x0F"); // SI for condensed resulting in pitch 20 cpi
break;
default:
alert("pitch unknown -- not set");
break;
}
qz.append(str);
qz.appendHex("x0D"); //CR for carriage return
qz.appendHex("x1Bx40"); //ESC # for reset settings
window["qzDoneAppending"] = function() {
// Tell the apple to print.
qz.print();
// Remove any reference to this function
window['qzDoneAppending'] = null;
};
}
One can actually create the array of characters-to-print as a two dimension array and ommit the last column of \r\n as I think this is not well accepted by all dot matrix printers. In that case one should do
//for each line of the array
qz.append($line); //line-1 turned into string, line-2 etc
qz.appendHex("x0D"); //CR for carriage return after each line
One problem that I came across is that if the user chose 17CPI then every character holds less space in the paper. This means that you cannot depend on one space characters to spread your data across the entire page in this case.
If anyone thinks they can contribute to this they are most welcome.

Perl pack() function and "B" format character to Php

What does the "B" do in this pack statement from Perl code?
$hce_hash=pack('B*', $hce_hash);
Is there an equivalent function in PHP?
PHP’s pack doesn’t support a format of B*, but it does support H*. In Perl, you could emulate it with
sub pack_Bstar {
my($bits) = #_;
my $Hstar;
my $nybble = 0;
for (my $i = 0; $i < length $bits; ++$i) {
$nybble *= 2;
$nybble += int substr($bits, $i, 1);
if ($i % 4 == 3) {
$Hstar .= sprintf "%x", $nybble;
$nybble = 0;
}
}
my $pad = 4 - length($bits) % 4;
if ($pad != 4) {
$nybble = ($nybble << $pad);
$Hstar .= sprintf "%x", $nybble;
}
pack "H*", $Hstar;
}
The code above is not idiomatic Perl, but translation to PHP should be straightforward.
The H* format wants a hex string with high nybble (4 bits) first. The code above chews off four bits at a time to compute each nybble value. For example, for a bit string of 1011, tracing the algorithm gives
nybble = 0
nybble = 2 * 0 + 1 = 1
nybble = 2 * 1 + 0 = 2
nybble = 2 * 2 + 1 = 5
nybble = 2 * 5 + 1 = 11
10112 is indeed 1110, which is b16. If the last nybble is incomplete (between one and three bits), we left-shift the bit the appropriate number of places. This has the effect of zero-padding on the right.
Tests:
my #tests = (
["01001010011101010111001101110100" => "Just"],
["0110000101101110011011110111010001101000011001010111001" => "another"],
["01010000010010000101000000101111010100000110010101110010011011" => "PHP/Perl"],
["01101000011000010110001101101011011001010111001000101100" => "hacker,"],
);
for (#tests) {
my($input,$expect) = #$_;
my $got = pack_Bstar $input;
print "$input: ", ($got eq $expect ? "PASS" : "FAIL"), " ($got)\n";
}
Output:
01001010011101010111001101110100: PASS (Just)
0110000101101110011011110111010001101000011001010111001: PASS (another)
01010000010010000101000000101111010100000110010101110010011011: PASS (PHP/Perl)
01101000011000010110001101101011011001010111001000101100: PASS (hacker,)
pack 'B*', $s returns the bytes represented by the string of 0 and 1 characters that form up the string in $s. The value of $s is right-padded with zeros to a length divisible by 8 if necessary.
For example,
pack 'B*', '0100101000110101'
results in
chr(0b01001010) . chr(0b00110101);
As others have noted, PHP's pack() does not support the B template, which in Perl's pack() turns a bitstring, represented as a literal string of 0 and 1 characters, into a packed byte string with 8 bits per byte.
However, since PHP's pack() does support the H template, which does the same except for hexadecimal digits instead of bits, we can emulate Perl's B template in PHP by first using base_convert() to turn the bits into hex digits and then packing those:
function pack_B( $bits, $len = false ) {
// truncate input to desired length, if given:
if ( $len === false ) $len = strlen( $bits );
else $bits = substr( $bits, 0, $len );
// pad input with zeros to next multiple of 4 above $len:
$hexlen = (int)( ($len + 3) / 4 );
$bits = str_pad( $bits, 4*$hexlen, "0" );
// split input into chunks of 4 bits, convert each to hex and pack them:
$nibbles = str_split( $bits, 4 );
foreach ( $nibbles as $i => $nibble ) {
$nibbles[$i] = base_convert( $nibble, 2, 16 );
}
return pack( "H*", implode( "", $nibbles ) );
}
(The reason we can't just feed the whole input string to base_convert() is that it stores its intermediate result as a PHP float, and thus doesn't produce correct results for numbers too large to be accurately represented by a float. Doing it one hex digit at a time works fine, however.)

How does this code extract the signature?

I have to debug an old PHP script from a developer who has left the company. I understand the most part of the code, except the following function. My question: What does...
if($seq == 0x03 || $seq == 0x30)
...mean in context of extracting the signature out of an X.509 certificate?
public function extractSignature($certPemString) {
$bin = $this->ConvertPemToBinary($certPemString);
if(empty($certPemString) || empty($bin))
{
return false;
}
$bin = substr($bin,4);
while(strlen($bin) > 1)
{
$seq = ord($bin[0]);
if($seq == 0x03 || $seq == 0x30)
{
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else
{
$bin = substr($bin,2 + $bytes + $len);
}
}
else
{
return false;
}
}
return false;
}
An X.509 certificate contains data in multiple sections (called Tag-Length-Value triplets). Each section starts with a Tag byte, which indicates the data format of the section. You can see a list of these data types here.
0x03 is the Tag byte for the BIT STRING data type, and 0x30 is the Tag byte for the SEQUENCE data type.
So this code is designed to handle the BIT STRING and SEQUENCE data types. If you look at this part:
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else // $seq == 0x30
{
$bin = substr($bin,2 + $bytes + $len);
}
you can see that the function is designed to skip over Sequences (0x30), until it finds a Bit String (0x03), at which point it returns the value of the Bit String.
You might be wondering why the magic number is 3 for Bit String and 2 for Sequence. That is because in a Bit String, the first value byte is a special extra field which indicates how many bits are unused in the last byte of the data. (For example, if you're sending 13 bits of data, it will take up 2 bytes = 16 bits, and the "unused bits" field will be 3.)
Next issue: the Length field. When the length of the Value is less than 128 bytes, the length is simply specified using a single byte (the most significant bit will be 0). If the length is 128 or greater, then the first length byte has bit 7 set, and the remaining 7 bits indicates how many following bytes contain the length (in big-endian order). More description here. The parsing of the length field happens in this section of the code:
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
// length is greater than 127!
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
After that, $bytes contains the number of extra bytes used by the length field, and $len contains the length of the Value field (in bytes).
Did you spot the error in the code? Remember,
If the length is 128 or greater, then the first length byte has bit 7
set, and the remaining 7 bits indicates how many following bytes
contain the length.
but the code says $bytes = ($len & 0x0f), which only takes the lower 4 bits of the byte! It should be:
$bytes = ($len & 0x7f);
Of course, this error is only a problem for extremely long messages: it will work fine as long as the length value will fit within 0x0f = 15 bytes, meaning the data has to be less than 256^15 bytes. That's about a trillion yottabytes, which ought to be enough for anybody.
As Pateman says above, you just have a logical if, we're just checking if $seq is either 0x30 or 0x03.
I have a feeling you already know that though, so here goes. $seq is the first byte of the certificate, which is probably either the version of the certificate or the magic number to denote that the file is a certificate (also known as "I'm guessing this because 10:45 is no time to start reading RFCs").
In this case, we're comparing against 0x30 and 0x03. These numbers are expressed in hexadecimal (as is every number starting with 0x), which is base-16. This is just really a very convenient shorthand for binary, as each hex digit corresponds to exactly four binary bits. A quick table is this:
0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
...
E = 1110
F = 1111
Equally well, we could have said if($seq == 3 || $seq == 48), but hex is just much easier to read and understand in this case.
I'd hazard a guess that it's a byte-order-independent check for version identifier '3' in an x.509 certificate. See RFC 1422, p7. The rest is pulling the signature byte-by-byte.
ord() gets the value of the ASCII character you pass it. In this case it's checking to see if the ASCII character is either a 0 or end of text (according to this ASCII table).
0x03 and 0x30 are hex values. Look that up and you'll have what $seq is matching to

how to get length of integers in PHP ?

I want to get the length of integer values for validation in PHP.
Example:
Mobile numbers should be only 10 integer values. It should not be more than 10 or less than 10 and also it should not be included of alphabetic characters.
How can I validate this?
$num_length = strlen((string)$num);
if($num_length == 10) {
// Pass
} else {
// Fail
}
if (preg_match('/^\d{10}$/', $string)) {
// pass
} else {
// fail
}
This will work for almost all cases (except zero) and easily coded in other languages:
$length = ceil(log10(abs($number) + 1));
In my opinion, the best way is:
$length = ceil(log10($number))
A decimal logarithm rounded up is equal to length of a number.
If you are using a web form, make sure you limit the text input to only hold 10 characters as well to add some accessibility (users don't want to input it wrong, submit, get a dialog about their mistake, fix it, submit again, etc.)
Use intval function in loop,
See this example
<?php
$value = 16432;
$length=0;
while($value!=0) {
$value = intval($value/10);
$length++
}
echo "Length of Integer:- ".$length;
?>
$input = "03432 123-456"; // A mobile number (this would fail)
$number = preg_replace("/^\d/", "", $number);
$length = strlen((string) $number);
if ($number == $input && $length == 10) {
// Pass
} else {
// Fail
}
If you are evaluating mobile numbers (phone numbers) then I would recommend not using an int as your chosen data type. Use a string instead because I cannot forsee how or why you would want to do math with these numbers. As a best practice, use int, floats, etc, when you want/need to do math. Use strings when you don't.
From your question, "You want to get the lenght of an integer, the input will not accept alpha numeric data and the lenght of the integer cannot exceed 10. If this is what you mean; In my own opinion, this is the best way to achieve that:"
<?php
$int = 1234567890; //The integer variable
//Check if the variable $int is an integer:
if (!filter_var($int, FILTER_VALIDATE_INT)) {
echo "Only integer values are required!";
exit();
} else {
// Convert the integer to array
$int_array = array_map('intval', str_split($int));
//get the lenght of the array
$int_lenght = count($int_array);
}
//Check to make sure the lenght of the int does not exceed or less than10
if ($int_lenght != 10) {
echo "Only 10 digit numbers are allow!";
exit();
} else {
echo $int. " is an integer and its lenght is exactly " . $int_lenght;
//Then proceed with your code
}
//This will result to: 1234556789 is an integer and its lenght is exactly 10
?>
By using the assertion library of Webmozart Assert we can use their build-in methods to validate the input.
Use integerish() to validate that a value casts to an integer
Use length() to validate that a string has a certain number of characters
Example
Assert::integerish($input);
Assert::length((string) $input, 10); // expects string, so we type cast to string
As all assertions in the Assert class throw an Webmozart\Assert\InvalidArgumentException if they fail, we can catch it and communicate a clear message to the user.
Example
try {
Assert::integerish($input);
Assert::length((string) $input, 10);
} catch (InvalidArgumentException) {
throw new Exception('Please enter a valid phone number');
}
As an extra, it's even possible to check if the value is not a non-negative integer.
Example
try {
Assert::natural($input);
} catch (InvalidArgumentException) {
throw new Exception('Please enter a valid phone number');
}
I hope it helps 🙂
A bit optimazed answer in 2 or 3 steps depends if we allow negative value
if(is_int($number)
&& strlen((string)$number) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200 to 00:00:00.173900
//Code
}
Note that will allow negative up to 9 numbers like -999999999
So if we need skip negatives we need 3rd comparision
if(is_int($number)
&& $number >= 0
&& strlen((string)$number) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200
// to 00:00:00.173900 over 20 tests
}
Last case when we want from -1 000 000 000 to 1 000 000 000
if(is_int($number)
&& $number >= 0
&& strlen(str_replace('-', '', (string)$number)) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200
// to 00:00:00.173900 over 20 tests
}
For compare
First naswer with regex
if (preg_match('/^\d{10}$/', $number)) {
// Fastest test with 00:00:00.246200
}
** Tested at PHP 8.0.12
** XAMPP 3.3.0
** Ryzen 7 2700
** MSI Radeon RX 5700 8G
Tested like
$function = function($number)
{
if(is_int($number)
&& $number >= 0
&& strlen((string)$number) == 10)
{
return true;
}
}
$number = 1000000000;
$startTime = DateTime::createFromFormat('U.u', microtime(true);
for($i = 0; $i < 1000000000; $i++)
{
call_user_func_array($function, $args);
}
$endTime = DateTime::createFromFormat('U.u', microtime(true);
echo $endTime->diff($startTime)->format('%H:%I:%S.%F');

Categories