I have been working with a piece of code for youtube style URL's but I have found a bug and I am hoping someone can show me the most efficient way to fix it.
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out += strpos($index, $in[$t]) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
}
$out = strrev($out); // reverse
}
return $out;
}
The bug is only when encoding a single number 238328 as it is my base to the power of three. As a result it divides exactly and because of use of 'floor' it goes unnoticed and the script tries to add the 62nd character which does not exist and only produces a three character code rather than four... thus 'aa' is the result rather than 'aaab'.
Here is the problem part of the code:
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
And to make it even easier here is the call to get the error
echo alphaID(238328);
credits: Orginally written by Kevin Vanzonneveld: kevin dot vanzonneveld dot net, modified by Simon Franz: blog dot snaky dot org and optimised by Stackoverflows very own mattbasta
Here you go:
function preciseDivision($x,$y)
{
// Correct floor's failures by adding a bit of overhead
$epsilon = 0.00000001;
return floor(($x/$y) + $epsilon);
}
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out += strpos($index, $in[$t]) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = preciseDivision(log10($in),log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = preciseDivision($in, $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
}
$out = strrev($out); // reverse
}
return $out;
}
The problem here was not floor, but floating point precision. The division resulted 2.99999999, and the floor(2.999999) is equal to 2, not 3. This happens because limited size of floating point variables.
That's why it did not work.
I wrote a function preciseDivision, which automatically adds a very small value to the division, to get through this.
And I still believe that there should exist cleaner solutions to this url hashing problem. I'll see what I can do.
As per my answer to your other question, try replacing log10($in) / log10($base) with log($in, $base).
This avoids the inaccuracies associated with dividing the results of the two logarithms as floating point numbers and gives you the correct result.
Adding another answer as the first one is also working, though this one is cleaner.
I got rid of BC Math functions. If you're going to work with really large integers, this may not work. Otherwise, this is much cleaner solution:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
// A conversion from base $base to base 10
$out = 0; // End number
$shift = 1; // Starting shift
$len = strlen($in); // Length of string
for ($t = 0; $t < $len; $t++)
{
$out += strpos($index, $in[$t]) * $shift; // $out is a number form alphabet * base^shift
$shift *= $base; // increase shift
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
// A simple conversion from base 10 to base $base
while ($in > 0)
{
$remainder = $in % $base;
$in = intval(($in-$remainder)/$base);
$out .= $index[$remainder];
}
}
return $out;
}
The code is cleaner, and should be faster as well.
Now it is much easier to see that this is only conversion from base 10 to base $base (62?) and vica-versa.
It does not involve floating point division, thus it does not have the bug mentioned above.
If you need multiplying large integers, and so on, this can be implemented this way as well with some bright thinking.
Added BC Maths, as you said you need large integers
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
// A conversion from base $base to base 10
$out = '0'; // End number
$shift = 1; // Starting shift
$len = strlen($in); // Length of string
for ($t = 0; $t < $len; $t++)
{
$out = bcadd($out, bcmul(strpos($index, $in[$t]),$shift)); // $out is a number from alphabet * base^shift
$shift = bcmul($shift, $base); // increase shift
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
// A simple conversion from base 10 to base $base
while ($in > '0') // We're treating integer as a string, so BC math works
{
$remainder = bcmod($in,$base);
$in = bcdiv($in, $base);
$out .= $index[$remainder];
}
}
return $out;
}
Related
I need help to create a licence plate (6 character length) from different equal or unequal length of strings.
Example 1:
$str1 = "YE37";
$str2 = "TE37";
$str3 = "LYTE";
When I combine, it should give me "LYTE37". I must use all of them to formulate a plate. I can find the common longest sequence between $str1 and $str2 is "E37" but unsure "Y" or "T" comes first (i.e., whether "YTE37" or "TYE37")" then I can combine with $str3 using the longest common sequence ("YTE") which supposed to give me "LYTE37".
Example 2: "YLF3", "EYLF" and "YLF37" should give me "EYLF37".
I use the following function that finds the longest common sequence
$string_1="YE37";
$string_2="TE37";
$S =get_longest_common_subsequence($string_1, $string_2); // $S is "E37"
function get_longest_common_subsequence($string_1, $string_2)
{
$string_1_length = strlen($string_1);
$string_2_length = strlen($string_2);
$return = '';
if ($string_1_length === 0 || $string_2_length === 0)
{
// No similarities
return $return;
}
$longest_common_subsequence = array();
// Initialize the CSL array to assume there are no similarities
$longest_common_subsequence = array_fill(0, $string_1_length, array_fill(0, $string_2_length, 0));
$largest_size = 0;
for ($i = 0; $i < $string_1_length; $i++)
{
for ($j = 0; $j < $string_2_length; $j++)
{
// Check every combination of characters
if ($string_1[$i] === $string_2[$j])
{
// These are the same in both strings
if ($i === 0 || $j === 0)
{
// It's the first character, so it's clearly only 1 character long
$longest_common_subsequence[$i][$j] = 1;
}
else
{
// It's one character longer than the string from the previous character
$longest_common_subsequence[$i][$j] = $longest_common_subsequence[$i - 1][$j - 1] + 1;
}
if ($longest_common_subsequence[$i][$j] > $largest_size)
{
// Remember this as the largest
$largest_size = $longest_common_subsequence[$i][$j];
// Wipe any previous results
$return = '';
// And then fall through to remember this new value
}
if ($longest_common_subsequence[$i][$j] === $largest_size)
{
// Remember the largest string(s)
$return = substr($string_1, $i - $largest_size + 1, $largest_size);
}
}
// Else, $CSL should be set to 0, which it was already initialized to
}
}
// Return the list of matches
return $return;
}
I need an algorithm that uses these strings and creates a licence plate.
Could this be the Algorithm you are looking for? Quick-Test Here.
<?php
$str1 = "YE37";
$str2 = "TE37";
$str3 = "LYTE";
$strA = "YLF3";
$strB = "EYLF";
$strC = "YLF37";
function generatePlateNumber($str1, $str2, $str3) {
$plateNumber = '';
$arr = array($str1, $str2, $str3);
$arrStr = array();
foreach($arr as $str){
if(!preg_match("#\d#", $str)){
$arrStr[] = $str;
}
}
foreach($arr as $str){
if(preg_match("#\d#", $str)){
$arrStr[] = $str;
}
}
$chars = array_merge(str_split($arrStr[0]),
str_split($arrStr[1]),
str_split($arrStr[2]) );
$alphabets = [];
$numbers = [];
foreach($chars as $char){
if(is_numeric($char)){
$numbers[] = $char;
}else{
$alphabets[] = $char;
}
}
$alphabets = array_unique($alphabets);
$numbers = array_unique($numbers);
// BUILD THE PLATE NUMBER:
$plateNumber .= implode($alphabets) . implode($numbers);
return $plateNumber;
}
I have tried to write a basic merge sort in PHP involving a small array, yet the problem is it takes about a minute or so to execute, and returns:
Fatal error: Allowed memory size of 536870912 bytes exhausted (tried
to allocate 35 bytes) in /Users/web/www/merge.php on line 39
Does anyone have an idea where the code might be going wrong (if at all)? I've been staring at this for a good hour now.
<?php
$array = array(8,1,2,5,6,7);
print_array($array);
merge_sort($array);
print_array($array);
function merge_sort(&$list){
if( count($list) <= 1 ){
return $list;
}
$left = array();
$right = array();
$middle = (int) ( count($list)/2 );
// Make left
for( $i=0; $i < $middle; $i++ ){
$left[] = $list[$i];
}
// Make right
for( $i = $middle; $i < count($list); $i++ ){
$right[] = $list[$i];
}
// Merge sort left & right
merge_sort($left);
merge_sort($right);
// Merge left & right
return merge($left, $right);
}
function merge(&$left, &$right){
$result = array();
while(count($left) > 0 || count(right) > 0){
if(count($left) > 0 && count(right) > 0){
if($left[0] <= $right[0]){
$result[] = array_shift($left);
} else {
$result[] = array_shift($right);
}
} elseif (count($left) > 0){
$result[] = array_shift($left);
} elseif (count($right) > 0){
$result[] = array_shift($right);
}
}
print_array($result);exit;
return $result;
}
function print_array($array){
echo "<pre>";
print_r($array);
echo "<br/>";
echo "</pre>";
}
?>
In your merge function, you call count on right instead of $right. PHP assumes this is a string constant (at least in 5.3.9) and when casted into an array that always has one element. So count(right) is always one, and you never exit the first merge.
Try this approach. Instead of shifting it, slice.
Also, for in while loop for the merge function, you need to do an and && comparison instead
of ||
function mergeSort($array)
{
if(count($array) == 1 )
{
return $array;
}
$mid = count($array) / 2;
$left = array_slice($array, 0, $mid);
$right = array_slice($array, $mid);
$left = mergeSort($left);
$right = mergeSort($right);
return merge($left, $right);
}
function merge($left, $right)
{
$res = array();
while (count($left) > 0 && count($right) > 0)
{
if($left[0] > $right[0])
{
$res[] = $right[0];
$right = array_slice($right , 1);
}
else
{
$res[] = $left[0];
$left = array_slice($left, 1);
}
}
while (count($left) > 0)
{
$res[] = $left[0];
$left = array_slice($left, 1);
}
while (count($right) > 0)
{
$res[] = $right[0];
$right = array_slice($right, 1);
}
return $res;
}
Have a look at this, the algorithm is already implemented, using array_push and array splice instead of just array_shift.
http://www.codecodex.com/wiki/Merge_sort#PHP
I implement merge sort this way
function mergeSort($Array)
{
$len = count($Array);
if($len==1){
return $Array;
}
$mid = (int)$len / 2;
$left = mergeSort(array_slice($Array, 0, $mid));
$right = mergeSort(array_slice($Array, $mid));
return merge($left, $right);
}
function merge($left, $right)
{
$combined = [];
$totalLeft = count($left);
$totalRight = count($right);
$rightIndex = $leftIndex=0;
while ($leftIndex < $totalLeft && $rightIndex < $totalRight) {
if ($left[$leftIndex] > $right[$rightIndex]) {
$combined[]=$right[$rightIndex];
$rightIndex++;
}else {
$combined[] =$left[$leftIndex];
$leftIndex++;
}
}
while($leftIndex<$totalLeft){
$combined[]=$left[$leftIndex];
$leftIndex++;
}
while ($rightIndex<$totalRight){
$combined[] =$right[$rightIndex];
$rightIndex++;
}
return $combined;
}
Here is the class in PHP to implement the Merge Sort -
<?php
class mergeSort{
public $arr;
public function __construct($arr){
$this->arr = $arr;
}
public function mSort($l,$r){
if($l===null || $r===null){
return false;
}
if ($l < $r)
{
// Same as ($l+$r)/2, but avoids overflow for large $l and $r
$m = $l+floor(($r-$l)/2);
// Sort first and second halves
$this->mSort($l, $m);
$this->mSort($m+1, $r);
$this->merge($l, $m, $r);
}
}
// Merges two subarrays of $this->arr[]. First subarray is $this->arr[$l..$m]. Second subarray is $this->arr[$m+1..$r]
public function merge($l, $m, $r)
{
if($l===null || $m===null || $r===null){
return false;
}
$n1 = $m - $l + 1;
$n2 = $r - $m;
/* create temp arrays */
$L=array();
$R=array();
/* Copy data to temp arrays $L[] and $R[] */
for ($i = 0; $i < $n1; $i++)
$L[$i] = $this->arr[$l + $i];
for ($j = 0; $j < $n2; $j++)
$R[$j] = $this->arr[$m + 1+ $j];
/* Merge the temp arrays back into $this->arr[$l..$r]*/
$i = 0; // Initial index of first subarray
$j = 0; // Initial index of second subarray
$k = $l; // Initial index of merged subarray
while ($i < $n1 && $j < $n2)
{
if($L[$i] <= $R[$j])
{
$this->arr[$k] = $L[$i];
$i++;
}
else
{
$this->arr[$k] = $R[$j];
$j++;
}
$k++;
}
/* Copy the remaining elements of $L[], if there are any */
while($i < $n1)
{
$this->arr[$k] = $L[$i];
$i++;
$k++;
}
/* Copy the remaining elements of $R[], if there are any */
while($j < $n2)
{
$this->arr[$k] = $R[$j];
$j++;
$k++;
}
}
}
$arr = array(38, 27, 43, 5, 9, 91, 12);
$obj = new mergeSort($arr);
$obj->mSort(0,6);
print_r($obj->arr);
?>
I was looking for a optimized Mergesort algorithm in PHP. There are 5 algorithms in the answers, so I tested those, and mine too. Using PHP 7.2.7, these are the times:
Sorting 1000 random numbers:
Avanche 1 0.0396 seconds
Avanche 2 0.0347 seconds
Kartik 0.0291 seconds
Kripa 0.0282 seconds
Samuel 0.0247 seconds
Mine 0.0144 seconds
Sorting 10 random numbers:
Avanche 1 0.000222 seconds
Kartik 0.000216 seconds
Kripa 0.000159 seconds
Avanche 2 0.000144 seconds
Samuel 0.000128 seconds
Mine 0.000098 seconds
So, although I encourage to whom read it to make it faster (that was I was looking for, and I believe it can be done), I let you my implementation too, cause seems to be faster than the other answers:
//This function needs start and end limits
function mergeSortRec(&$a,$start,$end){
if($start<$end){
$center=($start+$end)>>1; //Binary right shift is like divide by 2
mergeSortRec($a, $start, $center);
mergeSortRec($a, $center+1, $end);
//Mixing the 2 halfs
$aux=array();
$left=$start; $right=$center;
//Main loop
while($left<$center && $right<=$end){
if($a[$left]<$a[$right]){
$aux[]=$a[$left++];
}else{
$aux[]=$a[$right++];
}
}
//Copy the rest of the first half
while($left<$center) $aux[]=$a[$left++];
//Copy the rest of the second half
while($right<=$end) $aux[]=$a[$right++];
//Copy the aux array to the main array
foreach($aux as $v) $a[$start++]=$v;
}
}
//This is the function easier to call
function mergeSort(&$a) {
mergeSortRec($a,0,count($a)-1);
}
If you post a new answer, let me a comment to test it and add it.
Edit: I did some new optimizations, for those looking for a better implementation.
Your merge sort accepts a list by reference
function merge_sort(&$list)
So you need to assign it the new merged and sorted list. So instead of
return merge($left, $right);
do
$list = $this->merge($left, $right);
That should do it, just remove the exit and fix the count variable
MergeSort in PHP
<?php
class Solution
{
function mergeSort(&$arr)
{
if(count($arr) > 1) {
$mid = floor(count($arr)/2);
$left = array_slice($arr, 0, $mid);
$right = array_slice($arr, $mid);
$this->mergeSort($left);
$this->mergeSort($right);
// Merge the results.
$i = $j = $k = 0;
while(($i < count($left)) && ($j < count($right))) {
if($left[$i] < $right[$j]) {
$arr[$k] = $left[$i];
$i++;
} else {
$arr[$k] = $right[$j];
$j++;
}
$k++;
}
while($i < count($left)) {
$arr[$k] = $left[$i];
$i++;
$k++;
}
while($j < count($right)) {
$arr[$k] = $right[$j];
$j++;
$k++;
}
}
}
}
$s = new Solution();
$tmp = [12, 7, 11, 13, 5, 6, 7];
$s->mergeSort($tmp);
print_r($tmp);
I would like to know how this function works so I can re-write it in ColdFusion. I've never programmed in PHP. I think, the function checks if $string is 11 numbers in length.
But how is it doing it?
Why is it using a loop instead of a simple len() function?
I read about str_pad(), and understand it. But what is the function of the loop iteration $i as a third argument? What's it doing?
if($this->check_fake($cpf, 11)) return FALSE;
function check_fake($string, $length)
{
for($i = 0; $i <= 9; $i++) {
$fake = str_pad("", $length, $i);
if($string === $fake) return(1);
}
}
The function is meant to validate a CPF, the Brazilian equivalent of a US SSN #. The CPF is 11 characters in length. Basically, I need to know what it's doing so I can write the function in Coldfusion.
If it's just a length, can't it be if (len(cpf) != 11) return false; ?
Here is the entire code snippet if it interests you:
<?
/*
*# Class VALIDATE - It validates Brazilian CPF/CNPJ numbers
*# Developer: André Luis Cupini - andre#neobiz.com.br
************************************************************/
class VALIDATE
{
/*
*# Remove ".", "-", "/" of the string
*****************************************************/
function cleaner($string)
{
return $string = str_replace("/", "", str_replace("-", "", str_replace(".", "", $string)));
}
/*
*# Check if the number is fake
*****************************************************/
function check_fake($string, $length)
{
for($i = 0; $i <= 9; $i++) {
$fake = str_pad("", $length, $i);
if($string === $fake) return(1);
}
}
/*
*# Validates CPF
*****************************************************/
function cpf($cpf)
{
$cpf = $this->cleaner($cpf);
$cpf = trim($cpf);
if(empty($cpf) || strlen($cpf) != 11) return FALSE;
else {
if($this->check_fake($cpf, 11)) return FALSE;
else {
$sub_cpf = substr($cpf, 0, 9);
for($i =0; $i <=9; $i++) {
$dv += ($sub_cpf[$i] * (10-$i));
}
if ($dv == 0) return FALSE;
$dv = 11 - ($dv % 11);
if($dv > 9) $dv = 0;
if($cpf[9] != $dv) return FALSE;
$dv *= 2;
for($i = 0; $i <=9; $i++) {
$dv += ($sub_cpf[$i] * (11-$i));
}
$dv = 11 - ($dv % 11);
if($dv > 9) $dv = 0;
if($cpf[10] != $dv) return FALSE;
return TRUE;
}
}
}
}
?>
it basically makes sure the number isn't:
11111111111
22222222222
33333333333
44444444444
55555555555
etc...
With comments:
function check_fake($string, $length)
{
// for all digits
for($i = 0; $i <= 9; $i++)
{
// create a $length characters long string, consisting of
// $length number of the number $i
// e.g. 00000000000
$fake = str_pad("", $length, $i);
// return true if the provided CPN is equal
if($string === $fake) return(1);
}
}
In short, it tests to see if the provided string is just the same digit repeated $length times.
After using sites like JSFiddle I noticed that they auto generate a random and unique URL made up of various upper and lower case characters.
We could benefit from this for our booking pages. How is it done?
This is not random, there based of the ID of your database record.
How it works:
basically you have a string that is unique but it can be decrypted to represent a number, you should look at it as a short encryption / decryption.
You have a function that would take an unique ID and then it creates a Unique 'short string' from that id, and then you can reverse the process to get the unique id from the short string.
Here's a snipped I have found of a website:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
$index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($passKey !== null)
{
/* Although this function's purpose is to just make the
* ID short - and not so much secure,
* with this patch by Simon Franz (http://blog.snaky.org/)
* you can optionally supply a password to make it harder
* to calculate the corresponding numeric ID */
for ($n = 0; $n<strlen($index); $n++)
{
$i[] = substr( $index,$n ,1);
}
$passhash = hash('sha256',$passKey);
$passhash = (strlen($passhash) < strlen($index)) ? hash('sha512',$passKey) : $passhash;
for ($n=0; $n < strlen($index); $n++)
{
$p[] = substr($passhash, $n ,1);
}
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
}
$base = strlen($index);
if ($to_num)
{
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++)
{
$bcpow = bcpow($base, $len - $t);
$out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
}
if (is_numeric($pad_up))
{
$pad_up--;
if ($pad_up > 0)
{
$out -= pow($base, $pad_up);
}
}
$out = sprintf('%F', $out);
$out = substr($out, 0, strpos($out, '.'));
}
else
{
// Digital number -->> alphabet letter code
if (is_numeric($pad_up))
{
$pad_up--;
if ($pad_up > 0)
{
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log($in, $base)); $t >= 0; $t--)
{
$bcp = bcpow($base, $t);
$a = floor($in / $bcp) % $base;
$out = $out . substr($index, $a, 1);
$in = $in - ($a * $bcp);
}
$out = strrev($out); // reverse
}
return $out;
}
Example
alphaID(9007199254740989); //-> PpQXn7COf
alphaID('PpQXn7COf', true); //-> 9007199254740989
there's a link to the script here: https://github.com/kvz/deprecated/blob/kvzlib/php/functions/alphaID.inc.php
Use the uniqid() function if you just want a random id of a specified length or use the md5() hash of your long url if you need something repeatable. (output always the same for a given input). Creating a short URL service using PHP and MySQL gives a good rundown of how to put it all together.
It is very simple.
your url is something like
www.domain.com/in/here/RANDOMPART
you rewrite www.domain.com/in/here/* to a script of your choice. In this script you can get the requested uri with the globals and you can split it and use the part after the last '/'. This is your RANDOMPART. yust check it against your db or whatever.
To create such a url you simply have to add somthing behind "www.domain.com/in/here/". You could genereate a nice hopefully unique string withmd5(uniqid(rand(), true)).
Do not forget to save this string in a db or whatever.
Hope, that helps.
Something along these lines:
<?php
$url_length = rand(10,20); //generate a random number between 10 and 20 for the length of the URL
$url = array($url_length);
for($i=0; $i < $url_length; $i++)
{
$char = 0;
while($char < 65 OR $char > 122 OR ($char > 91 AND $char < 97))
{
$char = rand(65,122);
}
$url[] = chr($char);
}
print_r($url);
?>
Note this is partially psuedo code. This creates a URL with a random length (somewhere between 10 and 20), and populates each character with a random ASCII code that cooresponds to a letter in the alphabet. There are a few "junk" characters between the upper and lower case letters in the ASCII table, so this will reroll a random number until a valid ASCII number is generated.
This assumes you are storing this URL in a database. To ensure that this string is unique, you need to check to see if it exists yet in the database. If not, then add it and you're fine. If it exists, then redo the process again until you get a unique string.
You could simply create an array of all potential characters then randomly pick out however many you want and return them as the URL "code"?
$arr = array('a','b','c','d');
$count = count($arr) - 1;
$str = $arr[rand(0, $coun)].$arr[rand(0, $coun)];
After some searching I found this youtube style url generator with encryption to hide the original id... however I am hoping to improve the efficiency as it will be used a lot. So far I have improved it by 20%... can anyone help me improve it more.
This is the original:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
$index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($passKey !== null) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
for ($n = 0; $n<strlen($index); $n++) {
$i[] = substr( $index,$n ,1);
}
$passhash = hash('sha256',$passKey);
$passhash = (strlen($passhash) < strlen($index))
? hash('sha512',$passKey)
: $passhash;
for ($n=0; $n < strlen($index); $n++) {
$p[] = substr($passhash, $n ,1);
}
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$a = floor($in / bcpow($base, $t));
$out = $out . substr($index, $a, 1);
$in = $in - ($a * bcpow($base, $t));
}
$out = strrev($out); // reverse
}
return $out;
}
Here is my modified code so far:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if ($passKey !== null) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
$len = strlen($index);
$passhash = hash('sha256',$passKey);
$passhash = (strlen($passhash) < $len)
? hash('sha512',$passKey)
: $passhash;
for ($n=0; $n < $len; $n++) {
$p[] = substr($passhash, $n ,1);
}
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$a = floor($in / bcpow($base, $t));
$out = $out . substr($index, $a, 1);
$in = $in - ($a * bcpow($base, $t));
}
$out = strrev($out); // reverse
}
return $out;
}
As you can see no major difference, only I removed the strlen from the for loops and stored it in a variable and precalculated the array for the index (a bit clumsy... but the array generation is what was making up the bulk of the computation).
Credit where its due: here is the original authors info:
* #author Kevin van Zonneveld
* #author Simon Franz
* #copyright 2008 Kevin van Zonneveld (kevin dot vanzonneveld dot net)
* #license www dot opensource dot org/licenses/bsd-license dot php New BSD Licence
* #version SVN: Release: $Id: alphaID.inc.php 344 2009-06-10 17:43:59Z kevin $
* #link kevin dot vanzonneveld dot net
I can't post the url's as I have to low reputation :S
I've made some slight optimizations to remove some extra CPU cycles here and there. Mostly things like unnecessary assignments, extra comparisons, etc. Also, strings can be treated as arrays, so I worked that in, too:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out += strpos($index, $in[$t]) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
}
$out = strrev($out); // reverse
}
return $out;
}
Edit: I've updated to include a cache. Try it now!
It looks like you've done a good job getting rid of the redundant calls.
It looks like there are a couple calls to bcpow($base, $t) that could be reduced to one...
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$newbase = bcpow($base, $t);
$a = floor($in / $newbase);
$out = $out . substr($index, $a, 1);
$in = $in - ($a * $newbase);
}
$out = strrev($out); // reverse
You probably won't notice a difference.
I think you should just generate an index and use it site wide.. let me explain why...
The hashing and everything above $base = strlen($index); is just to generate the index... you will have to use the same $passkey to decode a url. As you have no way of knowing which passkey was used you would have to make it site wide. Which would mean each call to generate the url will use the same passkey and thus generate the same index.
And so... I would strip the index generating code and generate a unique index and store it in the $index variable.