Better way to code this? - php

What would be the best, simplest way to code this:
I have a php document that gets most of the page requests (set up in routes config, using code igniter framework) and depending on the uri i want to show the user different content. Example:
http: // domain.tld/2010/
Should show one type of content
http: // domain.tld/2010-nov/
should show another type of content
http: // domain.tld/2010-nov-10/
should show yet another type of content
http: // domain.tld/2010-nov-10-blog-post-title/
should show once again another type of content
Everything else should be treated as if is a product, example:
http: // domain.tld/light-bulb/
and if such a product doesnt exist, its a 404
Below is the code I got at the moment, but I feel it is way too messy. Any suggestion how to make it simpler and more effective? (Tried to get it formatted correctly here but it seem a bit tricky to get code to align properly, a apologize)
Regards,
Jason
(had to add spaces in all my urls here because im new and not allowed to post that many urls)
$val is the uri (/2010-nov.......)
function show_what($val){
$arr=array("jan"=>01,"feb"=>02,"mar"=>03,"apr"=>04,"may"=>05,"jun"=>06,"jul"=>07,"aug"=>08,"sep"=>09,"oct"=>10,"nov"=>11,"dec"=>12);
// first check to see if the uri starts with a year (4 digits)
if(is_int((int)substr($val,0,4)) && (int)substr($val,0,4)!=0){
// Get all posts for specified YEAR
if(strlen($val)==4){
// Show all blog posts for specified year
// example: http: // domain.tld/2010/
// Get all posts for specified YEAR and MONTH
}elseif(strlen($val)==8 && substr($val,4,1)=="-" && array_key_exists(substr($val,5,3),$arr)){
// show all blog posts for specified year and month
// example: http: // domain.tld/2010-nov/
// Get all posts for specified YEAR, MONTH and DAY OR! get specified post
}elseif(strlen($val)>=11 && substr($val,4,1)=="-" && array_key_exists(substr($val,5,3),$arr) && substr($val,8,1)=="-" && is_int((int)substr($val,9,2)) && (int)substr($val,9,2)!=0){
// Get all posts for specified YEAR, MONTH and DAY
if(strlen($val)==11){
// show all blog posts for specified year, month and day
// example: http: // domain.tld/2010-nov-10/
// Get specified post
}elseif(substr($val,11,1)=="-"){
// show specified post or 404
// example: http: // domain.tld/2010-nov-10-blog-post-title/
}else{
// "Not a valid article url<Br/>";
// example: http: // domain.tld/2010-nov-10there-is-a-dash-missing-after-day/
}
}else{
// 404, not a real date
}
}else{
// show product with current uri or if it doesnt exist, 404.
}
}

I'm not a PHP guy and wouldn't actually know how to implement it on PHP but you should definitely look for URL Rewrite with mod_rewrite in Apache or URL Rewrite in IIS 7 and take advantage of Regular Expressions so you don't need to parse strings.

You can use regular expressions to parse the URL part. For example:
(?<Year>[0-9]{4})
(-
(?<Month>[a-zA-Z]+)
(-
(?<Day>[0-9]{1,2})
(-
(?<Slugg>.*)
)?
)?
)?
(Almost reminds you of Lisp, doesn't it?)
Depending upon which parts are present and valid, perform the appropriate logic.
Here's a tested example to get you started. It includes my regexp solution and a solution using string splitting as suggested by others.
<?php
function getParts($source) {
$re = '/^
(?<Year>[0-9]{4})
(-
(?<Month>[a-zA-Z]+)
(-
(?<Day>[0-9]{1,2})
(-
(?<Slugg>.*)
)?
)?
)?
$/';
$re = str_replace(array(' ', "\n", "\r", "\t"), '', $re); // Strip whitespace that made the RE readable
$matches = null;
if (!preg_match($re, $source, $matches)) {
return array('title' => $source);
}
$ret = array();
if (!$matches['Year']) {
return $ret;
}
$ret['year'] = (int) $matches['Year'];
if (!$matches['Month']) {
return $ret;
}
$monthToNumber = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' =>>
$monthName = strtolower($matches['Month']);
if (!array_key_exists($monthName, $monthToNumber)) {
return $ret;
}
$ret['month'] = $monthToNumber[$monthName];
if (!$matches['Day']) {
return $ret;
}
$ret['day'] = (int) $matches['Day'];
if (!$matches['Slugg']) {
return $ret;
}
$ret['title'] = $matches['Slugg'];
return $ret;
}
function getParts2($source) {
$ret = array();
$errorRet = array('title' => $source);
$rawParts = explode('-', $source, 4);
if (count($rawParts) < 1 || !is_numeric($rawParts[0])) {
return $errorRet;
}
$ret['year'] = (int) $rawParts[0];
if (count($rawParts) < 2) {
return $ret;
}
$monthToNumber = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' =>>
$monthName = strtolower($rawParts[1]);
if (!array_key_exists($monthName, $monthToNumber)) {
return $errorRet;
}
$ret['month'] = $monthToNumber[$monthName];
if (count($rawParts) < 3) {
return $ret;
}
$ret['day'] = (int) $rawParts[2];
if (count($rawParts) < 4) {
return $ret;
}
$ret['title'] = $rawParts[3];
return $ret;
}
function test($testFunc, $source, $expected) {
$actual = call_user_func($testFunc, $source);
if ($actual !== $expected) {
echo "Test failed;\n";
echo "Input: ";
var_dump($source);
echo "Actual: ";
var_dump($actual);
echo "Expected: ";
var_dump($expected);
}
}
foreach (array('getParts', 'getParts2') as $testFunc) {
test($testFunc, '2010', array('year' => 2010));
test($testFunc, '2010-nov', array('year' => 2010, 'month' => 11));
test($testFunc, '2010-nov-10', array('year' => 2010, 'month' => 11, 'day' => 10));
test($testFunc, '2010-nov-10-blog-post-title', array('year' => 2010, 'month' => 11, 'day' => 10, 'title' => 'blog-post-title'));
test($testFunc, 'light-bulb', array('title' => 'light-bulb'));
}

you can simple explode it to array
$array = explode('-',$val);
and make an switch case of the array size like
switch(count($array){
# is like 2010
case 1:
// Show all blog posts for specified year
// example: http: // domain.tld/2010/
$year = $array[0];
break;
.....
}

Split the part on hyphens, like this:
$parts = explode('-', $url)
if (count($parts) == 2) {
$year = $parts[0];
$month = $parts[1];
} else if (count($parts) == 3) {
etc.

You should really take a look at the CodeIgniter Framework. Things like URL ReWrite and such are all build into the Framework and it's easy to use + lightning fast. I'm using it since 2008.

Related

Split Number by a number format

I truly thinking too long to find the logic/ algorithm to do this thing. Then I just use if else but I know this is bad because there will be too much statement.
I have number format group below to split the input number :
01 (Get 14 digits after this number format)
3101 (Get 6 digits after this number format)
3102 (Get 6 digits)
3202 (Get 6 digits)
13 (Get 6 digits)
15 (Get 6 digits)
11 (Get 6 digits)
21 (Get the rest)
Some of rules :
01 always in first sequence
21 always in last sequence
other number format except 01 and 21 can be in any sequence position.
The same prefix number format cannot be repeat
example, Input Number : 010069008517306731020020001319100421191004091395
The Result Should be :
01 : 00690085173067
3102 : 002000
13 : 191004
21 : 191004091395
Currently I only use IF ELSE statement to get the digits after.
This is my pieces of code using PHP. This code can only handle that example input above. There will be possibility of other sequence number format as per rules, but it will difficult if only use if else statement like this.
$first = substr($input, 0, 2);
if ($first == 01) {
$itemCode = substr($input, 2, 14); // get the 6 digits after 01
$second = substr($input, 16, 4);
if ($second == 3102) {
$quantity = substr($input, 20, 6); // get the 6 digits after 3102
$third = substr($input, 26, 2);
if ($third == 13) {
$packedDate = substr($input, 28, 6); // get the 6 digits after 13
$fourth = substr($input, 34, 2);
if ($fourth == 21) {
$serialNumber = substr($scanner, 36); // get the rest number after 21
}
}
}
}
Is there any good way to solve this thing?
If the prefixes won't repeat, you can use preg_match_all to match the prefixes with each of their trailing digits, using array_combine to create arrays of digits indexed by their prefixes:
$input = '010069008517306731020020001319100421191004091395';
if (preg_match_all('/(01)(\d{14})|(310[12]|3202|1[135])(\d{6})|(21)(\d+)/', $input, $matches)) {
$numbers = array_filter(array_combine($matches[1], $matches[2]) +
array_combine($matches[3], $matches[4]) +
array_combine($matches[5], $matches[6]));
print_r($numbers);
}
else {
echo "Invalid input!";
}
Output:
Array
(
[01] => 00690085173067
[3102] => 002000
[13] => 191004
[21] => 191004091395
)
Demo on 3v4l.org

How to write to new file once certain line is reached in php

I am trying to write a php script that will process each line of an m3u file and write it to the corresponding hour file. Whenever the process begins we always start at hour 00 or 12am midnight. Everything from the first line until the line that says END-OF-HOUR goes into file $month$day-$hour.58.15.m3u
$month and $day are to stay constant during this entire process and do successfully. Where I run into my problem is when I hit the END-OF-HOUR line. What is suppose to happen is that the script switches $hour from 00 to 01. The preceding 0 is very important for hours 0-9. Once the switch occurs it will start writing from the next line in the file to the hour 01 file until it hits the END-OF-HOUR line again. Once again increasing in hour value.
This needs to continue for all 24 hours of the day.
What is happening is that this script is copying the master file all into the hour 00 file.
Here is what I was able to do on my own:
<?php
//$location="";
$file="PLAYLIST";
$month="Nov";
$day="28";
$hour="00";
$outputlocation="Processed";
$outputfile="$month$day-$hour.58.15";
//Create Playlist Files Code Here and Working//
$handle = fopen("$file.m3u", "r");
if ($handle) {
while (($line = fgets($handle)) !== false) {
// process the line read.
//Begin Processing
//If End Of Hour
if ($line=="END-OF-HOUR"){
//If Not 11PM
if ($hour !=="23"){
$hour="$hour" + 1;
}
//If 11PM
if ($hour =="24"){
echo "<script>alert('MusicMaster File Processing Complete')</script>";
}
}
//If Not End Of Hour
if ($line !="END-OF-HOUR"){
$ofile=file_get_contents("$outputlocation\\$outputfile.m3u");
$nfile="$ofile
$line";
file_put_contents("$outputlocation\\$outputfile.m3u", "$nfile");
}
}
fclose($handle);
} else {
// error opening the file.
echo "<script>alert('Error Opening MusicMaster File')</script>";
}
//https://stackoverflow.com/questions/13246597/how-to-read-a-file-line-by-line-in-php
?>
I'm not well versed in looping in php. just very basic if statements and mysql queries.
This is the file it pulls from and outputs to each hour. This is only a snippet:
M:\JINGLES\TOH\LEGAL ID 20170416-A.mp3
M:\ITUNES\Music\Danny Gokey\Rise (Album)\02 If You Ain't In It.mp3
M:\ITUNES\Music\MercyMe\MercyMe, It's Christmas\06 Have a Holly Jolly Christmas.mp3
M:\JINGLES\STANDARD\Stay Tuned.mp3
M:\ITUNES\Music\Royal Tailor\Royal Tailor\06 Ready Set Go.mp3
M:\ITUNES\Music\Third Day\Revelation\03 Call My Name.mp3
M:\THE STORY BEHIND IT\Mandisa - Bleed The Same (Song Story).mp3
M:\PROMOTIONS\Valley Park Flea Market & Resale (6PM 5-29).mp3
M:\PROMOTIONS\FoundationLyrics_com.mp3
M:\PROMOTIONS\VinVlogger_com (5-15-17).mp3
END-OF-HOUR
M:\JINGLES\TOH\LEGAL ID 20170816.mp3
M:\ITUNES\Music\Audio Adrenaline\Kings & Queens\02 Kings & Queens.mp3
M:\ITUNES\Music\Stars Go Dim\Stars Go Dim\01 Doxology.mp3
M:\JINGLES\STANDARD\LIN\LIN-002.mp3
M:\ITUNES\Music\NewSong\Newsong\Christian.mp3
M:\ITUNES\Music\David Dunn\Crystal Clear - EP\02 Have Everything.m4a
M:\THE STORY BEHIND IT\Mandisa - Bleed The Same (Song Story).mp3
M:\PROMOTIONS\Valley Park Flea Market & Resale (6PM 5-29).mp3
END-OF-HOUR
I know I'm doing something wrong and just can't seem to figure out what it is. Any help you can provide would be very much appreciated.
I would start by changing this.
$outputfile="$month$day-$hour.58.15";
This needs to be updated while the while loop is iterating ( or at least when you change the hour )
Right now you are just using the initial value you set for hour 00 the entire time.
This is why you get the behaviour you have of it not changing the hour, because it's value is never re-assigned as the loop runs.
UPDATE
I took the liberty to rewrite your code. Sorry I'm a perfectionist, the more I looked at the more I didn't like it. ( not tested, as I don't have any files )
$file="PLAYLIST";
//Use an array, it's more concise and readable
$date =[
'month' => "Nov",
'day' => 28,
'hour' => 0,
'minute' => 58, //added for extendability
'second' => 15 //added for extendability
];
$outputlocation="Processed";
/*** Create Playlist Files Code Here and Working ***/
//open file. We can't proceed without the file, might as well stop here if we can't open it.
if(false === ($handle = fopen("$file.m3u", "r"))) die("Failed to open file.");
//while each line in the file
while (($line = fgets($handle)) !== false) {
if(trim(strtoupper($line)) =="END-OF-HOUR"){//If $line = End Of Hour
//trim removes whitespace from front and back, strtoupper should be self explanitory
if($hour < 24 ){
//if less the 12pm (and $line = 'END-OF-HOUR' )
//increment hour and left pad.
//you may need to use < 23 your logic forgot about it.
++$date['hour'];
}else{
//else if 12pm (and $line = 'END-OF-HOUR' )
echo "<script>alert('MusicMaster File Processing Complete')</script>";
}
continue;
/*
goes to next line ( iteration of the loop )
none of the code below this runs.
logically this is essentially what you had ^
so there is no need to continue
*/
}
// 0 pad left any parts that are len of 1 lenght
$fixed = array_map(function($i){
return (strlen($i) == 1) ? "0$i":$i;
}, $date);
/*
create the filename just before we use it
not that it matter in PHP, but the original array stays as INT's
the month is strlen() = 3, so it's unchanged by the above.
*/
$outputfile = $fixed['month'].$fixed['day'].'-'.$fixed['hour'].'.'.$fixed['minute'].'.'.$fixed['second'];
//this is all you..
$ofile=file_get_contents("$outputlocation\\$outputfile.m3u");
$nfile="$ofile
$line";
file_put_contents("$outputlocation\\$outputfile.m3u", "$nfile");
} //end while
I tested a few things with this:
$date =[
'month' => "Nov",
'day' => 28,
'hour' => 0,
'minute' => 58, //added for extendability
'second' => 15 //added for extendability
];
$fixed = array_map(function($i){
return (strlen($i) == 1) ? "0$i":$i;
}, $date);
$outputfile = $fixed['month'].$fixed['day'].'-'.$fixed['hour'].'.'.$fixed['minute'].'.'.$fixed['second'];
print_r($fixed);
echo "\n$outputfile\n";
Outputs
Array
(
[month] => Nov
[day] => 28
[hour] => 00
[minute] => 58
[second] => 15
)
Nov28-00.58.15
You can try it in this sandbox
UPDATE
If you wan't to trim all the lines, then just separate this
while (($line = fgets($handle)) !== false) {
if(trim(strtoupper($line)) =="END-OF-HOUR"){//If $line = End Of Hour
Like this
while (($line = fgets($handle)) !== false) {
$line = trim($line);
if(strtoupper($line) =="END-OF-HOUR"){//If $line = End Of Hour
A few other things about trim,
you can set the character that it trims, by setting the second argument, like this trim('**foo**', '*'); //outputs 'foo'
you can set more then one character but it acts like OR and replaces each one regardless the order such as trim('abcFOOcba', 'abc'); //outputs 'FOO'
you can trim just the right with rtrim(' Foo '); //outputs ' Foo' or trim just the left with ltrim(' Foo '); //outputs 'Foo '
I don't know why they have 3 separate functions, i'd prefer this trim($string, $match, $flag); where flag is TRIM_RIGH, TRIM_LEFT, TRIM_BOTH but, I guess you can't get everything you want. ( like the MySql version )
You can trim an array for white space pretty easily, by using array_map
$a = [ 'Foo ', ' Bar '];
$a = array_map('trim', $a);
print_r($a); //outputs ['Foo', 'Bar']
Documentation for PHP Trim
MySQL also as a TRIM() function SELECT TRIM(BOTH ' ' FROM column) AS foo They are very useful.
Documentation for Mysql Trim

convert number to word in php not working

Please note, the following PHP code converts amount in number to amount in words. This works fine for integer like (ex. 5250) and when it with decimal it gives wrong results.
Ex: for 450 = four hundred fifty only.
Ex: for 450.5 = four thousand five hundred five (which is wrong) and it should be four hundred fifty and fifty paisa only.
I have checked in the web analysed but unable to rectify the code. Can any body suggest /correct the code please?
<?php
function inr($number){
//A function to convert numbers into words.
$words = array(
'0'=> '' ,'1'=> 'one' ,'2'=> 'two' ,'3' => 'three','4' => 'four','5' => 'five',
'6' => 'six','7' => 'seven','8' => 'eight','9' => 'nine','10' => 'ten',
'11' => 'eleven','12' => 'twelve','13' => 'thirteen','14' => 'fouteen','15' => 'fifteen',
'16' => 'sixteen','17' => 'seventeen','18' => 'eighteen','19' => 'nineteen','20' => 'twenty',
'30' => 'thirty','40' => 'fourty','50' => 'fifty','60' => 'sixty','70' => 'seventy',
'80' => 'eighty','90' => 'ninty');
//First find the length of the number
$number_length = strlen($number);
//Initialize an empty array
$number_array = array(0,0,0,0,0,0,0,0,0);
$received_number_array = array();
//Store all received numbers into an array
for($i=0;$i<$number_length;$i++){ $received_number_array[$i] = substr($number,$i,1); }
//Populate the empty array with the numbers received - most critical operation
for($i=9-$number_length,$j=0;$i<9;$i++,$j++){ $number_array[$i] = $received_number_array[$j]; }
$number_to_words_string = "";
//Finding out whether it is teen ? and then multiplying by 10, example 17 is seventeen, so if 1 is preceeded with 7 multiply 1 by 10 and add 7 to it.
for($i=0,$j=1;$i<9;$i++,$j++){
if($i==0 || $i==2 || $i==4 || $i==7){
if($number_array[$i]=="1"){
$number_array[$j] = 10+$number_array[$j];
$number_array[$i] = 0;
}
}
}
$value = "";
for($i=0;$i<9;$i++){
if($i==0 || $i==2 || $i==4 || $i==7){ $value = $number_array[$i]*10; }
else{ $value = $number_array[$i]; }
if($value!=0){ $number_to_words_string.= $words["$value"]." "; }
if($i==1 && $value!=0){ $number_to_words_string.= "Crores "; }
if($i==3 && $value!=0){ $number_to_words_string.= "Lakhs "; }
if($i==5 && $value!=0){ $number_to_words_string.= "Thousand "; }
if($i==6 && $value!=0){ $number_to_words_string.= "Hundred "; }
}
if($number_length>9){ $number_to_words_string = "Sorry This does not support more than 99 Crores"; }
return ucwords(strtolower("Rupees ".$number_to_words_string)." Only.");
}
?>
you need to explode values after decimal point like
$numberarr = explode('.',$number);
$number = $numberarr[0];
$number_length = strlen($number);
//output Rupees Four Hundred Fifty Only.
also need more work on $numberarr[1] //we get after (.) values
why you have written $my_fig=inr(450.5) what is inr() function ?
try using intval() replacing inr()..
The problem starts here:
$number_length = strlen($number);
Consider the following numbers:
1234
12.3
The two numbers are clearly different orders of magnitude, but since your code centers around the string length, it treats the numbers as though they were the same order of magnitude. Because of this bad assumption, the answer returned is incorrect.
To fix the problem, recognize that a different system is used to create word values for the numbers before the decimals than after. For this reason, you need to first split the number at the decimal and process the two parts separately.
$integer_value = floor($number);
if (strstr($number, '.')) {
$decimal_value = (int)substr($number, strpos($number, '.'));
// Convert $decimal_value to its word value
}
There are well-tested libraries that will do all of this for you, so unless this is just a learning exercise I suggest that you make use of one of those.

Laravel and Charting - Determine The Difference Between Datatimes

I've been trying to create a chart similar to http://bit.ly/1jF6AY7 for a few weeks now using the Highcharts library. However, it does not understand that there should be zero values (if no value / date is provided) for the following series:
12-18-13
01-01-14
01-03-14
01-06-14
01-15-14
However, clearly using Google charts it is possible as the above graph is showing the gaps properly between the "contributed" dates. I need to do the same for my created_at dates. Any suggestions? I'd prefer to do this using Highcharts, but at this point my stress and hair is more valuable than anything else ;)
Vanilla PHP code example:
$q = $_GET['search'];
if(isset($_GET['time'])) {
$time = $_GET['time'];
} else {
$time = NULL;
}
include_once('../../vendors/HighchartsPHP-master/Highchart.php');
$chart = new Highchart(Highchart::HIGHSTOCK);
$chart->includeExtraScripts();
$chart->chart->renderTo = "chart";
$chart->title->text = "Potential Impact for \${$q}";
$chart->rangeSelector->selected = 1;
$chart->xAxis->type = 'datetime';
$chart->xAxis->gapGridLineWidth = 3;
$chart->xAxis->dateTimeLabelFormats->hour = "%H:%M";
$chart->xAxis->dateTimeLabelFormats->month = "%e. %b";
$chart->xAxis->dateTimeLabelFormats->year = "%b";
// $chart->xAxis->tickInterval = 3600 * 1000;
$chart->yAxis->title->text = "Volume";
$chart->yAxis->min = 0;
$chart->credits->enabled = false;
// $chart->rangeSelector->buttons[] = array(
// 'type' => "minute",
// 'count' => 5,
// 'text' => "5"
// );
...
$chart->rangeSelector->buttons[] = array(
'type' => "month",
'count' => 6,
'text' => "6M"
);
$chart->rangeSelector->buttons[] = array(
'type' => "year",
'count' => 1,
'text' => "1Y"
);
$chart->rangeSelector->buttons[] = array(
'type' => "all",
'text' => "All"
);
$option = new HighchartOption();
$option->global->useUTC = false;
Highchart::setOptions($option);
$chart->series[] = array(
'name' => "Potential Impact for \${$q}",
'data' => new HighchartJsExpr("data"),
'step' => 1,
'gapSize' => 5,
'connectNulls' => false,
'tooltip' => array(
'valueDecimals' => 0
)
);
$data = format_data($q, $time);
$chart->series[0]->data = $data;
?>
<div id="chart"></div>
<script type="text/javascript"><?php echo $chart->render("chart1"); ?></script>
And, the format data function:
function format_data($q, $time = NULL) {
$msg_vol = correlate_created_at_with_msg_vol($q, $time);
while ($tweet = mysqli_fetch_array($msg_vol, MYSQL_ASSOC)) {
$date = $tweet['date'] * 1000;
$count = intval($tweet['count']);
$formatted[] = array($date, $count);
}
return $formatted;
}
In the above picture, you can see not only is it graphing single data points (as illustrated by the tooltip) without showing any visual reference on the chart, but it is skipping the null periods, and not displaying the respective times properly on the xAxis. The odd part is that if you reference the bottom bar which allows the user to change the time period by dragging the bar... That bar has the gaps properly illustrated like what I'm looking for.
If I can't get Highcharts to go to a zero value for the gaps, I at the very least, need it to show the time intervals without jumping by an unpredictable number of minutes, hours, and in some cases, days and weeks.
If you use a datetime based series and there is no data for those points highcharts should handle it. Without seeing your code I image you are doing a category based xAxis and you are not "binning" any data in this time slot.
Edited:
Quick glance at your pasted code shows you are using HighStock, this is good. You can use the ordinal setting. Set this to false and you should see the gaps. Now it is still hard to say as I cannot see what your data is really but this should give us something to go on. You may also want to enable connectNulls.
Easy Solution, force 0 if there is no data.

Custom date and number format string (with padding and offsets)

I tried to create a customizable number according to a defined mask.
This is my rules to make a mask
You may enter any numbering mask. In this mask, the following tags could be used: {000000} corresponds to a number which will be incremented on each customer.
Enter as many zeros as the desired length of the counter.
The counter will be completed by zeros from the left in order to have as many zeros as the mask.
{000000+000} same as previous but an offset corresponding to the number to the right of the + sign is applied starting on first .
{000000#x} same as previous but the counter is reset to zero when month x is reached (x between 1 and 12).
If this option is used and x is 2 or higher, then sequence {yy}{mm} or {yyyy}{mm} is also required.
{dd} day (01 to 31).
{mm} month (01 to 12).
{yy}, {yyyy} or {y} year over 2, 4 or 1 numbers.
All other characters in the mask will remain intact.
Spaces are not allowed.
Example on customer created on 2007-03-01:
ABC{yy}{mm}-{000000} will give ABC0701-000099,
{0000+100}-ZZZ/{dd}/XXX will give 0199-ZZZ/31/XXX
So my current mask is C{000000}
<?php
$mask = "C{000000}";
$number = 100;
if (preg_match('/\{(0+)([#\+][0-9]+)?([#\+][0-9]+)?\}/i',$mask,$regType)){
$masktype=$regType[1];
$masktype_value=substr(preg_replace('/^TE_/','',$number),0,strlen($regType[1]));//get n first characters of code where n is length in mask
$masktype_value=str_pad($masktype_value,strlen($regType[1]),"#",STR_PAD_RIGHT);
$maskwithonlyymcode=$mask;
$maskwithonlyymcode=preg_replace('/\{(0+)([#\+][0-9]+)?([#\+][0-9]+)?\}/i',$regType[1],$maskwithonlyymcode);
$maskwithonlyymcode=preg_replace('/\{dd\}/i','dd',$maskwithonlyymcode);
$maskwithonlyymcode=preg_replace('/\{(c+)(0*)\}/i',$maskrefclient,$maskwithonlyymcode);
$maskwithonlyymcode=preg_replace('/\{(t+)\}/i',$masktype_value,$maskwithonlyymcode);
$maskwithnocode=$maskwithonlyymcode;
$maskwithnocode=preg_replace('/\{yyyy\}/i','yyyy',$maskwithnocode);
$maskwithnocode=preg_replace('/\{yy\}/i','yy',$maskwithnocode);
$maskwithnocode=preg_replace('/\{y\}/i','y',$maskwithnocode);
$maskwithnocode=preg_replace('/\{mm\}/i','mm',$maskwithnocode);
print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
}
?>
But it is not working it is printing
maskwithonlyymcode=C000000 maskwithnocode=C000000
My desired output is C000001 - C000100.
What is missing in this code?
I do not understand your code much, so I was not able to fix it, but what about:
<?
function process_mask($mask, $number, $date)
{
while (preg_match("/\{(.+?)\}/", $mask, $match))
{
$outter_code = $match[0];
$inner_code = $match[1];
if (preg_match("/^(0+)(\+(\d+))?$/", $inner_code, $match2))
{
$number2 = $number;
if (!empty($match2[3]))
{
$number2 += intval($match2[3]);
}
$replacement = str_pad($number2, strlen($match2[1]), "0", STR_PAD_LEFT);
}
else
{
switch ($inner_code)
{
case "dd":
$replacement = date("d", $date);
break;
case "mm":
$replacement = date("m", $date);
break;
case "y":
$replacement = substr(date("Y", $date), 3);
break;
case "yy":
$replacement = date("y", $date);
break;
case "yyyy":
$replacement = date("Y", $date);
break;
default:
trigger_error("Unrecognised code $inner_code");
return NULL;
}
}
$mask = str_replace($outter_code, $replacement, $mask);
}
return $mask;
}
function test_mask($mask)
{
$date = mktime(0, 0, 0, 4, 19, 2013);
echo str_pad($mask, 25)." => ".process_mask($mask, 100, $date)."\n";
}
test_mask("C{000}");
test_mask("C{000000}");
test_mask("C{000000+10}");
test_mask("ABC{yy}{mm}-{000000}");
test_mask("{0000+100}-ZZZ/{dd}/XXX");
?>
Outputs:
C{000} => C100
C{000000} => C000100
C{000000+10} => C000110
ABC{yy}{mm}-{000000} => ABC1304-000100
{0000+100}-ZZZ/{dd}/XXX => 0200-ZZZ/19/XXX
I absolutely do not undertand your rules about resetting counters. Based on what date do you want to reset the numbers? Current date? Do you keep some counter per customer (you have not explained what the number is)? Why resetting it on certain month? Wouldn't it be more meaningful to reset it in intervals? Like every month (implementation-wise, it would make sense then to keep separate counter for every month, so that the formatting logic is current time-independent). Some example may help understanding this.
Also for date formatting, I would suggest you to stick with PHP date formatting and do not invent your own.
I would suggest you to use pattern like this instead (It's actually bit .NET-like):
{(#[+offset]|php-date-format-string)[:length]}
So (for number = 999 and date = 2013-04-19):
C{#:4} => C0999
C{#+10:4} => C1009
C{#:6} => C000999
C{#:4}/{Y} => C0999/2013
C{#:4}/{Y:4} => C0999/2013
C{#:4}/{Y:2} => C0999/13
C{#:4}/{Y:1} => C0999/3
C{#:4}/{m} => C0999/03
C{#:4}/{Y}{m} => C0999/201303
C{#:4}/{Ym} => C0999/201303
Code for this would be way simpler, more extensible and flexible.

Categories