What is the best way to validate a credit card in codeigniter - php

hi i am using codeigniter . i want to validate my credit card details . i saw there are classes in php to validate credit card numbers . i saw a helper in codeigniter to validate credit cards
http://codeigniter.com/wiki/Credit_Card_Helper
/**
* Truncates a card number retaining only the first 4 and the last 4 digits. It then returns the truncated form.
*
* #param string The card number to truncate.
* #return string The truncated card number.
*/
function truncate_card($card_num) {
$padsize = (strlen($card_num) < 7 ? 0 : strlen($card_num) - 7);
return substr($card_num, 0, 4) . str_repeat('X', $padsize). substr($card_num, -3);
}
/**
* Validates a card expiry date. Finds the midnight on first day of the following
* month and ensures that is greater than the current time (cards expire at the
* end of the printed month). Assumes basic sanity checks have already been performed
* on month/year (i.e. length, numeric, etc).
*
* #param integer The expiry month shown on the card.
* #param integer The expiry year printed on the card.
* #return boolean Returns true if the card is still valid, false if it has expired.
*/
function card_expiry_valid($month, $year) {
$expiry_date = mktime(0, 0, 0, ($month + 1), 1, $year);
return ($expiry_date > time());
}
/**
* Strips all non-numerics from the card number.
*
* #param string The card number to clean up.
* #return string The stripped down card number.
*/
function card_number_clean($number) {
return ereg_replace("[^0-9]", "", $number);
}
/**
* Uses the Luhn algorithm (aka Mod10) <http://en.wikipedia.org/wiki/Luhn_algorithm>
* to perform basic validation of a credit card number.
*
* #param string The card number to validate.
* #return boolean True if valid according to the Luhn algorith, false otherwise.
*/
function card_number_valid ($card_number) {
$card_number = strrev(card_number_clean($card_number));
$sum = 0;
for ($i = 0; $i < strlen($card_number); $i++) {
$digit = substr($card_number, $i, 1);
// Double every second digit
if ($i % 2 == 1) {
$digit *= 2;
}
// Add digits of 2-digit numbers together
if ($digit > 9) {
$digit = ($digit % 10) + floor($digit / 10);
}
$sum += $digit;
}
// If the total has no remainder it's OK
return ($sum % 10 == 0);
}
?>
it uses a common validation . but i want a validation according to card type like this
http://www.braemoor.co.uk/software/creditcard.php
is there any libraries or helpers in codeigniter . please help.....................

As people already told you, CodeIgniter is a php framework, coded using php, works in a php environment and makes use of..,php classes and functions :).
What's more, the file you linked to is a simple function. One function. You know what you can do? Take the file as it is, name it creditcard_helper.php, put it inside the helpers folder, open it and place the whole code inside this snippet (ugly but necessary, as whenever you'll load the helper a second time it would give you error otherwise):
if(!function_exists('checkCreditCard')
{
//the whole content goes here untouched;
}
And you're set. Just use:
$this->load->helper('creditcard');
if(checkCreditCard($cardnumber, $cardname, &$errornumber, &$errortext))
{
echo 'card OK';
}
else
{
echo 'wrong card type/number';
}

I found this helper inside the CodeIgniter Github wiki page. When dropped inside your helpers folder, you can then use the functions from the file in a controller or model you load it in.

Related

Match a whitelist of IP addresses in PHP

I have a list of IP ranges like
$whitelist=array('50*','202.16*','123.168*',');
I want to block all other traffic from seeing the page.
I've tried
if(in_array($_SERVER['REMOTE_ADDR'],$whitelist)){
//display page
}
in_array doesn't use regexs to compare. Also your regex is incorrect the * is a quantifier. That allows zero or more of the previous character.
Try:
$whitelist=array('50\..*','202\.16\..*','123\.168\..*');
if(preg_match('/^(' . implode('|', $whitelist) . ')/', $_SERVER['REMOTE_ADDR'])){
The .* is allowing anything (pretty much(see s modifier http://php.net/manual/en/reference.pcre.pattern.modifiers.php), . is any character and then paired with that quantifier previously mentioned). The ^ is the start of the string. \. is a literal .. The | is an or.
Demo: https://eval.in/571019
Regex Demo: https://regex101.com/r/dC5uI0/1
This should work for you:
you can loop through white list ip and trim space and * (if found from right).
There after using substr you can cut the IP address of the same length of whitelist ip in loop, and compare both.
$whitelists = array('50*','202.16*','123.168*');
foreach($whitelists as $whitelist){
$whitelist = rtrim($whitelist, "*\r\n\t\0 ");
if(substr($_SERVER['REMOTE_ADDR'], 0, strlen($whitelist)) == $whitelist) {
$match = true;
break;
}
}
echo $match ? 'Match' : 'Not Match';
as stated by #chris85, in_array doesn't use regexs.
To do such a thing, you could simply use a loop like this:
if(preg_match('/^(' . implode('|', $whitelist) . ')/i', $_SERVER['REMOTE_ADDR'])){
// Do your stuff
}
Your '*' doesn't work as you thought.. That's ok:
$whitelist=array('50\.*','202.16\.*','123.168\.*');
Hope this helps someone. Seeing as how you didn't specifically ask about regular expressions, and since the topic is about matching IP addresses, I thought I'd put this out there so that it may help someone with a similar issue.
Server software usually strives to be as quick and efficient as possible; matching IP addresses is usually done arithmetically. That being said, I'll go over a fast way to do exactly what you're trying to do before going over a possible alternative.
If you're simply performing a wild card match on IP address strings, I would suggest that you use this method. It has been tailored to your use case, but I will include the simple matching function by itself.
For contrast, I've also included the sample output with execution times using this method compared to using PHP's RegEx functions (which are the way to go for more complex pattern matching)
NOTES:
I use the functions referenced herein for very specific purposes. They are applicable to this scenario because IP addresses don't have '*' characters in them. As they are written, if the variable you're testing has a '*' character in it, it will only match against a wild card character so there is a slight possibility for loss of information there.
If you're writing a command-line daemon or your process will use the same list of IPs to check against multiple times during its lifetime, it is beneficial to use the RegEx library. The small speed benefit to using my method here is only won when having to initially load and prepare the IP list RegEx for first use.
You can move the code from "wildcard_match()" to inside of "match_ip() for further benefit, avoiding the overhead of another function call.
Code (you can copy and paste):
<?php
/**
* This function compares a string ("$test") to see if it is
* equal to another string ("$wild"). An '*' in "$wild" will
* match any characters in "$test".
*
* #param string $wild - The string to compare against. This may
* be either an exact character string to match, or a string
* with a wild card ('*') character that will match any character(s)
* found in "$test".
*
* #param string $test - A character string we're comparing against
* "$wild" to determine if there is a match.
*
* #return bool Returns TRUE if "$test" is either an exact match to
* "$wild", or it fits the bill taking any wild card characters into
* consideration.
*
**/
function wildcard_match( $pattern, $test ) {
$p = 0;
$a_name = explode("*", $pattern);
$segs = count($a_name);
$max_seg = ($segs-1);
$plen = 0;
$test_len = strlen($test);
for ($i = 0; $i < $segs; $i++) {
$part = $a_name[$i];
$plen = strlen($part);
if ($plen === 0) {
if ($i === $max_seg) return true;
continue;
}
$p = strpos($test, $part, $p);
if ($p === false) {
return false;
}
$p+=$plen;
}
if ($p===$test_len) {
return true;
}
return false;
}
/**
* Function to quickly traverse an array of whole, or
* wild card IPv4 addresses given in "$whitelist" and
* determine if they match the given whole IPv4
* address in "$test".
*
* #param array $whitelist - An array of IPv4 addresses, either
* whole, or containing an '*' character wherein any character(s)
* in "$test" will match.
*
* #param string $test - A complete string (dot-decimal) IPv4
* address to compare against the contents of the array given in
* parameter one ("$whitelist").
*
* #return bool Returns TRUE, if the IPv4 address given in "$test" was
* matched to an IPv4 address or IPv4 wild card pattern in the array
* given in parameter one ("$whitelist").
*
**/
function match_ip( $whitelist, $test ) {
foreach ($whitelist as $w) {
if (wildcard_match($w, $test)) return true;
}
return false;
}
/* The array of IP addresses we're going to validate */
$check_array = array("50.245.1.9", "35.125.25.255", "202.16.15.25");
/* The array as given in your example (minus the extra ' at the end) */
$whitelist1=array('50*','202.16*','123.168*');
/* An array for RegEx matching */
$whitelist2=array('50\..*','202\.16\..*','123\.168\..*');
microtime(true); /* Execute this once to make sure its module is loaded */
echo "Giving PHP a second to get its ducks in a row...\n";
usleep(1000000); /** Give PHP a second to load and prepare */
$st = microtime(true);
foreach ($check_array as $c) {
if (match_ip($whitelist1, $c)) {
echo "$c....Match!\n";
} else {
echo "$c....No match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n\n\n";
$st = microtime(true);
foreach ($check_array as $c) {
if(preg_match('/^(' . implode('|', $whitelist2) . ')/', $c)){
echo "$c....Match!\n";
} else {
echo "$c....No Match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n";
The output from this is:
Giving PHP a second to get its ducks in a row...
50.245.1.9....Match!
35.125.25.255....No match!
202.16.15.25....Match!
Time 1: 0.00027704238891602
50.245.1.9....Match!
35.125.25.255....No Match!
202.16.15.25....Match!
Time 2: 0.00040698051452637
The first result set is from the function "match_ip()", the second result set is from firing up the RegEx library.
Now, a possible better solution to wild card matching IPs would be to employ an array of IP addresses in CIDR notation in your array. Often times, you want IP traffic to be allowed from a specific network or range of IP addresses.
There are quite a few assumptions here, but for example (using your "$whitelist" array):
'50*' might be construed as "I want all IP addressess from 50.xxx.xxx.xxx to be allowed access.
In that case, you'd specify the format "50.0.0.0/8". (The "0"s after the "50." can be any number. They will be completely ignored because of the "/8".)
xxx.xxx.xxx.xxx
| | | |
8 16 24 32
An IPv4 address is computationally 32 bits, so above, you're saying that all you care about is the first 8 bits matching.
'123.168*' would be "123.168.0.0/16"
"101.23.54.0/24" would allow all IP addresses starting with "101.23.54" access.
"44.32.240.10/32" would only allow the IP address "44.32.240.10" access. No range.
So you could do something like this:
<?php
/**
* Determines if the two given IPv4 addresses
* are equal, or are on the same network using
* the given number of "$mask" bits.
*
* #param string $ip1 - The first string dot-decimal IPv4
* address.
*
* #param string $ip1 - The second string dot-decimal IPv4
* address.
*
* #param int $mask - The number of bits in the mask.
*
* #return bool Returns TRUE if they match after being
* masked, or FALSE if not.
*
*/
function ip_match( $ip1, $ip2, $mask) {
$mask = (int)$mask;
$ip1 = ip2long($ip1);
$ip2 = ip2long($ip2);
if ($ip1 === false || $ip2 === false) return false;
if ($mask < 1 || $mask > 32) return false;
$mask = (0x00000000FFFFFFFF & 0x00000000FFFFFFFF << (32-$mask));
if ( ($ip1 & $mask) === ($ip2 & $mask) ) {
return true;
}
return false;
}
/**
* Takes an array of string (CIDR) network representations and
* sorts them into an array used later for checking against IP
* addresses.
*
* #param array $cidr_array - An array of IP addressess in
* CIDR notation e.g. 192.168.1.1/24
*
* #return array Returns an array of objects with the following
* properties:
* 'ip' - The string (dot-decimal) IP address that
* has been numerically verified for use
* in comparisons later.
*
* 'mask' - The number of bits used for creating
* the subnet mask against which IP
* addresses will be compared.
*
**/
function make_whitelist( $cidr_array ) {
$wl = array();
$lip = 0;
$bm = 0;
$spos = 0;
if (!is_array($cidr_array)) return false;
foreach ($cidr_array as $ip) {
$spos = strpos($ip, "/");
if ($spos === false) {
$bm = 32; /* If there's no "/", assume
* that we want an EXACT IP
* address. Hence the 32 bit
* mask
**/
} else {
$bm = (int)substr($ip, ($spos+1));
$ip = substr($ip, 0, $spos++);
}
$lip = ip2long($ip); /* Using this here to check IP validity
* before storing it in the array...
* We use ip2long() later for comparisons.
*
* You can store it this way - as a long -
* instead of as a string (I do) to
* use less memory if you wish.
*
**/
if ($bm === 0) continue; /* A bit mask of ZERO will block
* ALL IP addresses, skip it
* for the example.
**/
if ($lip === false) continue; /* If it's an invalid IP, skip it,
* you could optionally try to
* resolve it as a hostname using
* gethostbyname() or gethostbynamel()
* here...
**/
array_push($wl, (object)array('ip'=>$ip, 'mask'=>$bm));
}
return $wl;
}
$whitelist = make_whitelist(array("50.0.0.0/8", "202.16.0.0/16", "123.168.0.0/16", "1.1.1.1"));
$ips_to_check = array("50.1.174.41", "42.123.100.23", "123.168.4.79", "1.1.1.2", "1.1.1.1");
foreach ($ips_to_check as $ip) {
foreach ($whitelist as $w) {
if (ip_match($ip, $w->ip, $w->mask)) {
echo "\"$ip\" is allowed!\n";
continue 2;
}
}
echo "\"$ip\" is NOT allowed!\n";
}
I know this is a lot, but there is plenty here for people to think about, find while searching, and hopefully be able to use to make their lives easier!

How to calculate the "perfect" column widths

So, here's the plan: I'm using TCPDF to generate PDF documents containing a table. I'm generating an html table in PHP which I pass to TCPDF. However, TCPDF makes each column's width equal, which is a problem, as the content length in each column is quite different. The solution is to set the width attribute on the table's <td>s. But I can't quite workout the perfect way to do so. That's what I'm currently doing:
I generate an array called $maxColumnSizes in which I store the maximum number of letters per column.
I generate an array called $averageSizes in which I store the average number of letters per column.
So, below you see an example calculation. Column 0 has 8 letters average, and 26 letters at max, column 4 has 10 letters average, and 209 letters at max:
So, here's the problem: I can't think of the "right" way to combine this information to get the "perfect" column widths. If I ignore the $maxColumnSizes array and set the column widths based on the $averageSizes, the table looks quite good. Except for the one row where Column 4 has 209 characters. As Column 4 is pretty small, the row where there are 209 characters has an insane height, to fit the 209 characters in.
To sum it up: How do I calculate the "perfect" table column width (given the table data)?
Notes:
"perfect width" for me means that the whole table's height is as small as possible.
I currently do not take letter-widths into account (I do not differentiate between the width of an i and a w)
As I have access to all the data, I can also make any other calculations needed. The two arrays I mention above I only used in my first tries.
EDIT
Based on the comment I added another calculation calculating $maxColumnSize / $averageColumnSize:
This is rather subjective, but to take a stab at an algorithm:
// Following two functions taken from this answer:
// http://stackoverflow.com/a/5434698/697370
// Function to calculate square of value - mean
function sd_square($x, $mean) { return pow($x - $mean,2); }
// Function to calculate standard deviation (uses sd_square)
function sd($array) {
// square root of sum of squares devided by N-1
return sqrt(array_sum(array_map("sd_square", $array, array_fill(0,count($array), (array_sum($array) / count($array)) ) ) ) / (count($array)-1) );
}
// For any column...
$colMaxSize = /** from your table **/;
$colAvgSize = /** from your table **/;
$stdDeviation = sd(/** array of lengths for your column**/);
$coefficientVariation = $stdDeviation / $colAvgSize;
if($coefficientVariation > 0.5 && $coefficientVariation < 1.5) {
// The average width of the column is close to the standard deviation
// In this case I would just make the width of the column equal to the
// average.
} else {
// There is a large variance in your dataset (really small values and
// really large values in the same set).
// What to do here? I would base the width off of the max size, perhaps
// using (int)($colMaxSize / 2) or (int)($colMaxSize / 3) to fix long entries
// to a given number of lines.
}
There's a PECL extension that gives you the stats_standard_deviation function, but it is not bundled with PHP by default. You can also play around with the 0.5 and 1.5 values above until you get something that looks 'just right'.
Based on #watcher's answer, I came up with the following code. It works great in my test cases. I also made a GitHub repository with my code, as it is far better readable than here on StackOverflow.
<?php
/**
* A simple class to auto-calculate the "perfect" column widths of a table.
* Copyright (C) 2014 Christian Flach
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This is based on my question at StackOverflow:
* http://stackoverflow.com/questions/24394787/how-to-calculate-the-perfect-column-widths
*
* Thank you "watcher" (http://stackoverflow.com/users/697370/watcher) for the initial idea!
*/
namespace Cmfcmf;
class ColumnWidthCalculator
{
/**
* #var array
*/
private $rows;
/**
* #var bool
*/
private $html;
/**
* #var bool
*/
private $stripTags;
/**
* #var int
*/
private $minPercentage;
/**
* #var Callable|null
*/
private $customColumnFunction;
/**
* #param array $rows An array of rows, where each row is an array of cells containing the cell content.
* #param bool $html Whether or not the rows contain html content. This will call html_entity_decode.
* #param bool $stripTags Whether or not to strip tags (only if $html is true).
* #param int $minPercentage The minimum percentage each row must be wide.
* #param null $customColumnFunction A custom function to transform a cell's value before it's length is measured.
*/
public function __construct(array $rows, $html = false, $stripTags = false, $minPercentage = 3, $customColumnFunction = null)
{
$this->rows = $rows;
$this->html = $html;
$this->stripTags = $stripTags;
$this->minPercentage = $minPercentage;
$this->customColumnFunction = $customColumnFunction;
}
/**
* Calculate the column widths.
*
* #return array
*
* Explanation of return array:
* - $columnSizes[$colNumber]['percentage'] The calculated column width in percents.
* - $columnSizes[$colNumber]['calc'] The calculated column width in letters.
*
* - $columnSizes[$colNumber]['max'] The maximum column width in letters.
* - $columnSizes[$colNumber]['avg'] The average column width in letters.
* - $columnSizes[$colNumber]['raw'] An array of all the column widths of this column in letters.
* - $columnSizes[$colNumber]['stdd'] The calculated standard deviation in letters.
*
* INTERNAL
* - $columnSizes[$colNumber]['cv'] The calculated standard deviation / the average column width in letters.
* - $columnSizes[$colNumber]['stdd/max'] The calculated standard deviation / the maximum column width in letters.
*/
public function calculateWidths()
{
$columnSizes = array();
foreach ($this->rows as $row) {
foreach ($row as $key => $column) {
if (isset($this->customColumnFunction)) {
$column = call_user_func_array($this->customColumnFunction, array($column));
}
$length = $this->strWidth($this->html ? html_entity_decode($this->stripTags ? strip_tags($column) : $column) : $column);
$columnSizes[$key]['max'] = !isset($columnSizes[$key]['max']) ? $length : ($columnSizes[$key]['max'] < $length ? $length : $columnSizes[$key]['max']);
// Sum up the lengths in `avg` for now. See below where it is converted to the actual average.
$columnSizes[$key]['avg'] = !isset($columnSizes[$key]['avg']) ? $length : $columnSizes[$key]['avg'] + $length;
$columnSizes[$key]['raw'][] = $length;
}
}
// Calculate the actual averages.
$columnSizes = array_map(function ($columnSize) {
$columnSize['avg'] = $columnSize['avg'] / count ($columnSize['raw']);
return $columnSize;
}, $columnSizes);
foreach ($columnSizes as $key => $columnSize) {
$colMaxSize = $columnSize['max'];
$colAvgSize = $columnSize['avg'];
$stdDeviation = $this->sd($columnSize['raw']);
$coefficientVariation = $stdDeviation / $colAvgSize;
$columnSizes[$key]['cv'] = $coefficientVariation;
$columnSizes[$key]['stdd'] = $stdDeviation;
$columnSizes[$key]['stdd/max'] = $stdDeviation / $colMaxSize;
// $columnSizes[$key]['stdd/max'] < 0.3 is here for no mathematical reason, it's been found by trying stuff
if(($columnSizes[$key]['stdd/max'] < 0.3 || $coefficientVariation == 1) && ($coefficientVariation == 0 || ($coefficientVariation > 0.6 && $coefficientVariation < 1.5))) {
// The average width of the column is close to the standard deviation
// In this case I would just make the width of the column equal to the
// average.
$columnSizes[$key]['calc'] = $colAvgSize;
} else {
// There is a large variance in the dataset (really small values and
// really large values in the same set).
// Do some magic! (There is no mathematical rule behind that line, it's been created by trying different combinations.)
if ($coefficientVariation > 1 && $columnSizes[$key]['stdd'] > 4.5 && $columnSizes[$key]['stdd/max'] > 0.2) {
$tmp = ($colMaxSize - $colAvgSize) / 2;
} else {
$tmp = 0;
}
$columnSizes[$key]['calc'] = $colAvgSize + ($colMaxSize / $colAvgSize) * 2 / abs(1 - $coefficientVariation);
$columnSizes[$key]['calc'] = $columnSizes[$key]['calc'] > $colMaxSize ? $colMaxSize - $tmp : $columnSizes[$key]['calc'];
}
}
$totalCalculatedSize = 0;
foreach ($columnSizes as $columnSize) {
$totalCalculatedSize += $columnSize['calc'];
}
// Convert calculated sizes to percentages.
foreach ($columnSizes as $key => $columnSize) {
$columnSizes[$key]['percentage'] = 100 / ($totalCalculatedSize / $columnSize['calc']);
}
// Make sure everything is at least 3 percent wide.
if ($this->minPercentage > 0) {
foreach ($columnSizes as $key => $columnSize) {
if ($columnSize['percentage'] < $this->minPercentage) {
// That's how many percent we need to steal.
$neededPercents = ($this->minPercentage - $columnSize['percentage']);
// Steal some percents from the column with the $coefficientVariation nearest to one and being big enough.
$lowestDistance = 9999999;
$stealKey = null;
foreach ($columnSizes as $k => $val) {
// This is the distance from the actual $coefficientVariation to 1.
$distance = abs(1 - $val['cv']);
if ($distance < $lowestDistance
&& $val['calc'] - $neededPercents > $val['avg'] /* This line is here due to whatever reason :/ */
&& $val['percentage'] - $this->minPercentage >= $neededPercents /* Make sure the column we steal from would still be wider than $this->minPercentage percent after stealing. */
) {
$stealKey = $k;
$lowestDistance = $distance;
}
}
if (!isset($stealKey)) {
// Dang it! We could not get something reliable here. Fallback to stealing from the largest column.
$max = -1;
foreach ($columnSizes as $k => $val) {
if ($val['percentage'] > $max) {
$stealKey = $k;
$max = $val['percentage'];
}
}
}
$columnSizes[$stealKey]['percentage'] = $columnSizes[$stealKey]['percentage'] - $neededPercents;
$columnSizes[$key]['percentage'] = $this->minPercentage;
}
}
}
return $columnSizes;
}
/**
* Function to calculate standard deviation.
* http://stackoverflow.com/a/5434698/697370
*
* #param $array
*
* #return float
*/
protected function sd($array)
{
if (count($array) == 1) {
// Return 1 if we only have one value.
return 1.0;
}
// Function to calculate square of value - mean
$sd_square = function ($x, $mean) { return pow($x - $mean,2); };
// square root of sum of squares devided by N-1
return sqrt(array_sum(array_map($sd_square, $array, array_fill(0,count($array), (array_sum($array) / count($array)) ) ) ) / (count($array)-1) );
}
/**
* Helper function to get the (approximate) width of a string. A normal character counts as 1, short characters
* count as 0.4 and long characters count as 1.3.
* The minimum width returned is 1.
*
* #param $text
*
* #return float
*/
protected function strWidth($text)
{
$smallCharacters = array('!', 'i', 'f', 'j', 'l', ',', ';', '.', ':', '-', '|',
' ', /* normal whitespace */
"\xC2", /* non breaking whitespace */
"\xA0", /* non breaking whitespace */
"\n",
"\r",
"\t",
"\0",
"\x0B" /* vertical tab */
);
$bigCharacters = array('w', 'm', '—', 'G', 'ß', '#');
$width = strlen($text);
foreach (count_chars($text, 1) as $i => $val) {
if (in_array(chr($i), $smallCharacters)) {
$width -= (0.6 * $val);
}
if (in_array(chr($i), $bigCharacters)) {
$width += (0.3 * $val);
}
}
if ($width < 1) {
$width = 1;
}
return (float)$width;
}
}
That's it! $columnSizes[$colNumber]['percentage'] now includes a well fitting ("perfect") width for each column.

Code generator with Laravel

I am using Laravel 4 and I am looking for a system to generate simple short alphanumeric codes, kinda like what Steam does when you login from a new machine, they send you an email with some text like this:
Someone has tried to log in into your account from an unknown device.
If this is really you, please input this code to authenticate the
access:
ZT27K
Thank you
Is there some sort of random, short codes generator in Laravel?
Yup! A bit down on the helpers documentation page is the str_random() function, which can be used anywhere like so:
$string = str_random(5);
(Note: it defaults to 16 if its length argument is left out.)
This is how I implemented mine, thoughStr::random($length) would be better:
/**
* #param string prefix Any desired character to prepend on the generated code
* #param int length a number indicating the number of characters in the code
* #param string factor a string of characters to be mixed to generate the code
* #return string string with random characters based the provided length
*/
function generateCode($prefix = '', $length = 8, $factor = null)
{
$s = "9/A/B/0/C/2/D/E/3/F/G/H/I//J/4/K/L/M/N/O/5/P/7/R/S/8/T/U/V/6/W/X/Y/Z";
if ($factor === null)
$factor = $s;
$rdm_text_arr = explode("/", $factor, strlen($factor));
$code_array = array();
array_push($code_array, $prefix);
for ($i = 1; $i <= $length; $i++) :
array_push($code_array, $rdm_text_arr[mt_rand(0, count($rdm_text_arr) - 1)]);
endfor;
return implode("", $code_array);
}
and this is a sample output: WJH3Z0MX
You may also try Str::random(8) which gives 8 random characters.

Find Available time in a day of one person

I am doing resource allocation for an event.
My event table for one resource is looks like this:
(Int)---(nvarchar(100)---(datetime)--(datetime)
EventId --- Event --- StartTime --- EndTime
1 / test / 2013-02-20 13:00:00 / 2013-02-20 15:00:00
2 / test2 / 2013-02-20 09:30:00 / 2013-02-20 11:00:00
3 / test3 / 2013-02-25 11:30:00 / 2013-02-25 14:30:00
Now I want to find the total availablity for this resource on one day.
Like on 20th Feb 2013 I want to remove the busy hours from this resource and want to show only available hours for new event.
I am using php and sql server 2008 r2.
It works fine with only one record of a single day. Right now I am using a foreach loop with calculation.
My Code is:
$id = 6;
$cdata = $test->getResourceEvents($id);
$h = "";
$final= array();
foreach($cdata as $c)
{
$sh = $c['starttime']->Format('H'); // Starting hour
$eh = $c['endtime']->Format('H'); // End hour
$hh = $sh;
$final = array();
$sdate = $c['starttime']->Format('Y-m-d');
$edate = $c['endtime']->Format('Y-m-d');
if($edate == $sdate)
{
$dh = $eh-$sh; // Duration
for($i=1;$i<=$dh;$i++)
{
$hh = $hh.",".($sh+$i); // Busy hours
}
$busyhrs[$sdate] = explode(",",$hh);
$final[$sdate] = $busyhrs;
}
else
{
echo "false";
}
}
print_r($final);
and my result is :
Array
(
[2013-02-20] => Array
(
[2013-02-20] => Array
(
[0] => 9
[1] => 10
[2] => 11
)
)
[2013-02-26] => Array
(
[2013-02-26] => Array
(
[0] => 11
[1] => 12
[2] => 13
[3] => 14
)
)
)
First two records have the same dates. but this only calculates 2nd row's hours. Not calculating first row's hours that is 13,14,15.
Can anyone please tell me how to match the dates and how to get total busy hours of one date?
I think this does what you're expecting. The key lines being those involving $all_finals and the moving of $final. I usually write in python and didn't know the best way to append to an array in php so I used this http://php.net/manual/en/function.array-push.php
$id = 6;
$cdata = $test->getResourceEvents($id);
$h = "";
$all_finals = array();
foreach($cdata as $c)
{
$final= array();
$sh = $c['starttime']->Format('H'); // Starting hour
$eh = $c['endtime']->Format('H'); // End hour
$hh = $sh;
$final = array();
$sdate = $c['starttime']->Format('Y-m-d');
$edate = $c['endtime']->Format('Y-m-d');
if($edate == $sdate)
{
$dh = $eh-$sh; // Duration
for($i=1;$i<=$dh;$i++)
{
$hh = $hh.",".($sh+$i); // Busy hours
}
$busyhrs[$sdate] = explode(",",$hh);
$final[$sdate] = $busyhrs;
}
else
{
echo "false";
}
array_push($all_finals, $final);
}
print_r($all_final);
I did something similar to this in Java.
I had a table with date ranges in it, and a date range that I had to insert into the lapses in coverage from the date ranges already present.
I essentially took the date range I'm interested in, and "subtract" all the existing date ranges. You'll find my subtract method in the code below.
Subtracting DateRange_B from DateRange_A results in DateRange_A being modified in place, and the method returning a new DateRange if DateRange_A got entirely split by DateRange_B.
There are of course other ways to solve this, such iterations in SQL SERVER, but I was already in a Java mindset and this solution just kinda happened.
/*
* Copyright (c) 2009, Ben Fortuna
* (Modified by Alex Marunowski)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* o Neither the name of Ben Fortuna nor the names of any other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.Serializable;
import java.util.Date;
/**
* #author fortuna
*
*/
public class DateRange implements Serializable {
private static final long serialVersionUID = -7303846680559287286L;
/**
* A flag indicating whether to include the start of the period in test functions.
*/
public static final int INCLUSIVE_START = 1;
/**
* A flag indicating whether to include the end of the period in test functions.
*/
public static final int INCLUSIVE_END = 2;
private Date rangeStart;
private Date rangeEnd;
/**
* #param start the start of the range
* #param end the end of the range
*/
public DateRange(Date start, Date end) {
if (start == null) {
throw new IllegalArgumentException("Range start is null");
}
if (end == null) {
throw new IllegalArgumentException("Range end is null");
}
if (end.before(start)) {
throw new IllegalArgumentException("Range start must be before range end");
}
this.rangeStart = start;
this.rangeEnd = end;
}
/**
* #return the rangeStart
*/
public Date getStartDate() {
return rangeStart;
}
/**
* #return the rangeEnd
*/
public Date getEndDate() {
return rangeEnd;
}
public void setRangeStart(Date rangeStart) {
if(rangeStart.after(getEndDate()))
throw new IllegalArgumentException("The start of a date range cannot be after its end!");
this.rangeStart = rangeStart;
}
public void setRangeEnd(Date rangeEnd) {
if(rangeStart.after(getEndDate()))
throw new IllegalArgumentException("The end of a date range cannot be after its start!");
this.rangeEnd = rangeEnd;
}
/**
* Determines if the specified date occurs within this period (inclusive of
* period start and end).
* #param date a date to test for inclusion
* #return true if the specified date occurs within the current period
*
*/
public final boolean includes(final Date date) {
return includes(date, INCLUSIVE_START | INCLUSIVE_END);
}
/**
* Decides whether a date falls within this period.
* #param date the date to be tested
* #param inclusiveMask specifies whether period start and end are included
* in the calculation
* #return true if the date is in the period, false otherwise
* #see Period#INCLUSIVE_START
* #see Period#INCLUSIVE_END
*/
public final boolean includes(final Date date, final int inclusiveMask) {
boolean includes = true;
if ((inclusiveMask & INCLUSIVE_START) > 0) {
includes = includes && !rangeStart.after(date);
}
else {
includes = includes && rangeStart.before(date);
}
if ((inclusiveMask & INCLUSIVE_END) > 0) {
includes = includes && !rangeEnd.before(date);
}
else {
includes = includes && rangeEnd.after(date);
}
return includes;
}
/**
* Decides whether this period is completed before the given period starts.
*
* #param range
* a period that may or may not start after this period ends
* #return true if the specified period starts after this periods ends,
* otherwise false
*/
public final boolean before(final DateRange range) {
return (rangeEnd.before(range.getStartDate()));
}
/**
* Decides whether this period starts after the given period ends.
*
* #param range
* a period that may or may not end before this period starts
* #return true if the specified period end before this periods starts,
* otherwise false
*/
public final boolean after(final DateRange range) {
return (rangeStart.after(range.getEndDate()));
}
/**
* Decides whether this period intersects with another one.
*
* #param range
* a possible intersecting period
* #return true if the specified period intersects this one, false
* otherwise.
*/
public final boolean intersects(final DateRange range) {
boolean intersects = false;
// Test for our start date in period
// (Exclude if it is the end date of test range)
if (range.includes(rangeStart) && !range.getEndDate().equals(rangeStart)) {
intersects = true;
}
// Test for test range's start date in our range
// (Exclude if it is the end date of our range)
else if (includes(range.getStartDate())
&& !rangeEnd.equals(range.getStartDate())) {
intersects = true;
}
return intersects;
}
/**
* Decides whether these periods are serial without a gap.
* #param range a period to test for adjacency
* #return true if one period immediately follows the other, false otherwise
*/
public final boolean adjacent(final DateRange range) {
boolean adjacent = false;
if (rangeStart.equals(range.getEndDate())) {
adjacent = true;
} else if (rangeEnd.equals(range.getStartDate())) {
adjacent = true;
}
return adjacent;
}
/**
* Decides whether the given period is completely contained within this one.
*
* #param range
* the period that may be contained by this one
* #return true if this period covers all the dates of the specified period,
* otherwise false
*/
public final boolean contains(final DateRange range) {
// Test for period's start and end dates in our range
return (includes(range.getStartDate()) && includes(range.getEndDate()));
}
/**
* Decides whether the given period is completely contained within this one, taking into consideration whether date ranges with matching start or end dates
* are counted as being contained
*
* #param range
* the period that may be contained by this one
* #param inclusiveMask
* if this is set to 0, the start and end dates cannot be the same date and have it be considered to be contained within this date range.
* this.contains(this, 1) returns true
* this.contains(this, 0) returns false
* #return true if this period covers all the dates of the specified period,
* otherwise false
*/
public final boolean contains(final DateRange range, int inclusiveMask) {
// Test for period's start and end dates in our range
return (includes(range.getStartDate(), inclusiveMask) && includes(range.getEndDate(), inclusiveMask));
}
/**
* Exclude otherRange from the dates covered by this DateRange. Note: This will put the specified buffer around the range being subtracted from this date range.
* #param otherRange
* #return an additional date range if subtracting otherRange from this DateRange results in a part of this objects DateRange being separated from the rest of the range.
* i.e. if this.includes(otherRange, 0), then there will be two remaining portions of this daterange.
* If no dangling date range remains, then the method returns null.
* #author Alex Marunowski. 2012.10.31
*/
public DateRange subtract(DateRange otherRange, long buffer) throws DateRangeObliteratedException{
Date bufferedStart = new Date(otherRange.getStartDate().getTime()-buffer);
Date bufferedEnd= new Date(otherRange.getEndDate().getTime()+buffer);
otherRange = new DateRange(bufferedStart, bufferedEnd);
// If the other range is entirely after this range, nothing happens
if(getEndDate().before(otherRange.getStartDate()))
return null;
// If the other range is entirely before this range, nothing happens
if(getStartDate().after(otherRange.getEndDate()))
return null;
if(otherRange.contains(this))
throw new DateRangeObliteratedException();
DateRange separatedTimeInterval = null;
if(this.contains(otherRange, 0)){
// The trailing daterange is the time between the end date of the inner daterange, and the end date of the outer date range
separatedTimeInterval = new DateRange(otherRange.getEndDate(), getEndDate());
// This date range now ends at the start time of the inner date range
this.setRangeEnd(otherRange.getStartDate());
return separatedTimeInterval;
}
if(otherRange.getEndDate().before(getEndDate())){
// This date range is now the time between the end of the otherRange plus the buffer time, and the end of this date range
long newRangeStart = otherRange.getEndDate().getTime();
this.setRangeStart(new Date(newRangeStart));
return null;
}
if(otherRange.getStartDate().after(getStartDate())){
// This date range is now the time between this date range's start, and the other date ranges start minus the buffer time
long newRangeEnd = otherRange.getStartDate().getTime();
this.setRangeEnd(new Date(newRangeEnd));
return null;
}
// This will never happen, but the compiler doesn't know that
System.out.println("This should never have happened. No comparisons between the date ranges was discovered");
return null;
}
public static class DateRangeObliteratedException extends Exception {
/**
*
*/
private static final long serialVersionUID = -5642891561498447972L;
public DateRangeObliteratedException() {
super("This date range no longer exists. It was entirely contained within the range you subtracted from it.");
}
}
}
for(int rangeIndex = 0; rangeIndex less than rangesBusy.size(); rangeIndex++) {
DateRange busyRange = rangesBusy.get(rangeIndex);
try {
DateRange trailingRange = freeTimeBlock.subtract(busyRange, 0);
if(trailingRange != null) {
freeTimeRanges.add(trailingRange);
}
} catch (DateRangeObliteratedException e) {
freeTimeRanges.remove(rangeIndex);
rangeIndex--;
}
}
I found the solution.
I changed the process of making final array.
Thanks a lot to all for helping me.
I think its simple.
Here is my code.
May be it will be helpful to someone.
$id = 6;
$cdata = $test->getResourceEvents($id);
$h = "";
$final= array();
foreach($cdata as $c)
{
$sh = $c['starttime']->Format('H'); // Starting hour
$eh = $c['endtime']->Format('H'); // End hour
$hh = $sh;
$busyhrs = array();
$sdate = $c['starttime']->Format('Y-m-d');
$edate = $c['endtime']->Format('Y-m-d');
if($edate == $sdate)
{
$dh = $eh-$sh; // Duration
for($i=1;$i<=$dh;$i++)
{
$hh = $hh.",".($sh+$i); // Busy hours
}
if($final != array() || $final != NULL)
{
foreach($final as $key=>$val)
{
if($key==$sdate)
{
$final[$key] = $val.",".$hh;
}
else
{
$final[$sdate] = $hh;
}
}
}
else
{
$final[$sdate] = $hh;
}
}
else
{
echo "false";
}
}
echo "<pre>";
print_r($final);

Bitmask in PHP for settings?

Bits and bitmask are something I have been struggling to understand for a while, but I would like to learn how to use them for settings and things like that in PHP.
I have finally found a class that claims to do exactly that, and as I can tell, it seems to work, but I am not sure if it is the best way of doing this. I will post the class file with example code below to show it in working order.
Please if you have experience, tell me if it can be improved, for performance or anything else. I really want to learn this, and I have been reading up on it, but it is a difficult one for me to grasp so far.
The class...
<?php
class bitmask
{
/**
* This array is used to represent the users permission in usable format.
*
* You can change remove or add valuesto suit your needs.
* Just ensure that each element defaults to false. Once you have started storing
* users permsisions a change to the order of this array will cause the
* permissions to be incorectly interpreted.
*
* #type Associtive array
*/
public $permissions = array(
"read" => false,
"write" => false,
"delete" => false,
"change_permissions" => false,
"admin" => false
);
/**
* This function will use an integer bitmask (as created by toBitmask())
* to populate the class vaiable
* $this->permissions with the users permissions as boolean values.
* #param int $bitmask an integer representation of the users permisions.
* This integer is created by toBitmask();
*
* #return an associatve array with the users permissions.
*/
public function getPermissions($bitMask = 0)
{
$i = 0;
foreach ($this->permissions as $key => $value)
{
$this->permissions[$key] = (($bitMask & pow(2, $i)) != 0) ? true : false;
// Uncomment the next line if you would like to see what is happening.
//echo $key . " i= ".strval($i)." power=" . strval(pow(2,$i)). "bitwise & = " . strval($bitMask & pow(2,$i))."<br>";
$i++;
}
return $this->permissions;
}
/**
* This function will create and return and integer bitmask based on the permission values set in
* the class variable $permissions. To use you would want to set the fields in $permissions to true for the permissions you want to grant.
* Then call toBitmask() and store the integer value. Later you can pass that integer into getPermissions() to convert it back to an assoicative
* array.
*
* #return int an integer bitmask represeting the users permission set.
*/
function toBitmask()
{
$bitmask = 0;
$i = 0;
foreach ($this->permissions as $key => $value)
{
if ($value)
{
$bitmask += pow(2, $i);
}
$i++;
}
return $bitmask;
}
}
?>
How do I set/save the permissions as a bitmask value?
<?php
/**
* Example usage
* initiate new bitmask object
*/
$perms = new bitmask();
/**
* How to set permissions for a user
*/
$perms->permissions["read"] = true;
$perms->permissions["write"] = true;
$perms->permissions["delete"] = true;
$perms->permissions["change_permissions"] = true;
$perms->permissions["admin"] = false;
// Converts to bitmask value to store in database or wherever
$bitmask = $perms->toBitmask(); //in this example it is 15
$sql = "insert into user_permissions (userid,permission) values(1,$bitmask)";
echo $sql; //you would then execute code to insert your sql.
?>
Example of taking the bitmask value and returning a true/false for each array item based on the bit value....
<?php
/**
* Example usage to get the bitmask value from database or session/cache.... then put it to use.
* $permarr returns an array with true/false for each array value based on the bit value
*/
$permarr = $perms->getPermissions($bitmask);
if ($permarr["read"])
{
echo 'user can read: <font color="green">TRUE</font>';
}
else {
echo 'user can read: <font color="red">FALSE</font>';
}
//user can WRITE permission
if ($permarr["write"])
{
echo '<br>user can write: <font color="green">TRUE</font>';
}
else {
echo '<br>user can write: <font color="red">FALSE</font>';
}
?>
Bit fields are a very handy and efficient tool for dealing with flags or any set of boolean values in general.
To understand them you first need to know how binary numbers work. After that you should check out the manual entries on bitwise operators and make sure you know how a bitwise AND, OR and left/right shift works.
A bit field is nothing more than an integer value. Let's assume our bit field's size is fixed and only one byte. Computers work with binary numbers, so if the value of our number is 29, you'll actually find 0001 1101 in the memory.
Using bitwise AND (&) and bitwise OR (|) you can read out and set each bit of the number individually. They both take two integers as input and perform an AND/OR on each bit individually.
To read out the very first bit of your number, you could do something like this:
0001 1101 (=29, our number)
& 0000 0001 (=1, bit mask)
= 0000 0001 (=1, result)
As you can see you need a special number where only the bit we're interested in is set, that's the so called "bit mask". In our case it's 1. To read out the second bit we have to "push" the one in the bitmask one digit to the left. We can do that with the left shift operator ($number << 1) or by multiplying our by two.
0001 1101
& 0000 0010
= 0000 0000 (=0, result)
You can do that for every bit in our number. The binary AND of our number and the bit mask leads either to zero, which means the bit wasn't "set", or to a non-zero integer, which means the bit was set.
If you want to set one of the bits, you can use bitwise OR:
0001 1101
| 0010 0000 (=32, bit mask)
= 0011 1101 (=29+32)
However, you'll have to go a different way when you want to "clear" a bit.
A more general approach would be:
// To get bit n
$bit_n = ($number & (1 << $n)) != 0
// Alternative
$bit_n = ($number & (1 << $n)) >> $n
// Set bit n of number to new_bit
$number = ($number & ~(1 << $n)) | ($new_bit << $n)
At first it might look a bit cryptic, but actually it's quite easy.
By now you probably found out that bit fields are quite a low-level technique. That's why I recommend not to use them within PHP or databases.. If you want to have a bunch of flags it's probably ok, but for anything else you really don't need them.
The class you posted looks a bit special to me. For example, things like ... ? true : false are veery bad practice. If you want to use bit fields, you're probably better off defining some constants and use the method described above. It's not hard to come up with a simple class.
define('PERM_READ', 0);
define('PERM_WRITE', 1);
class BitField {
private $value;
public function __construct($value=0) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
public function get($n) {
return ($this->value & (1 << $n)) != 0;
}
public function set($n, $new=true) {
$this->value = ($this->value & ~(1 << $n)) | ($new << $n);
}
public function clear($n) {
$this->set($n, false);
}
}
$bf = new BitField($user->permissions);
if ($bf->get(PERM_READ)) {
// can read
}
$bf->set(PERM_WRITE, true);
$user->permissions = $bf->getValue();
$user->save();
I didn't try any piece of code of this answer, but it should get you started even if it isn't working out of the box.
Note that you're limited to 32 values per bit field.
Here's how to define bitmasks.
// the first mask. In binary, it's 00000001
define('BITWISE_MASK_1', 1 << 0); // 1 << 0 is the same as 1
// the second mask. In binary, it's 00000010
define('BITWISE_MASK_2', 1 << 1);
// the third mask. In binary, it's 00000100
define('BITWISE_MASK_3', 1 << 2);
To check if a bitmask is present (in this case in a function argument), use the bitwise AND operator.
function computeMasks($masks) {
$masksPresent = array();
if ($masks & BITWISE_MASK_1)
$masksPresent[] = 'BITWISE_MASK_1';
if ($masks & BITWISE_MASK_2)
$masksPresent[] = 'BITWISE_MASK_2';
if ($masks & BITWISE_MASK_3)
$masksPresent[] = 'BITWISE_MASK_3';
return implode(' and ', $masksPresent);
}
This works because when you OR two bytes (say, 00000001 and 00010000), you get the two together: 00010001. If you AND the result and the original mask (00010001 and say, 00000001), you get a the mask if it's present (in this case 00000001). Otherwise, you get zero.

Categories