I created this function in my laravel controller.
function incrementalHash($len = 5){
$charset = "0123456789abcdefghijklmnopqrstuvwxyz";
$base = strlen($charset);
$result = '';
$now = explode(' ', microtime())[1];
while ($now >= $base){
$i = $now % $base;
$result = $charset[$i] . $result;
$now /= $base;
}
return substr($result, -5);
}
then I have a function to insert something in the database. this function uses the above function. but every time I use it I get the same result from above function. I tried composer dump-autoload and the result changes. I wonder what is happening? why this method always returns the same result. how can I use this method and not receive the same result without dumping autoload? here is my controller:
public function add_user_create()
{
$user = new User;
$user->user_id = Request()->input('user_id');
$user->user_name = Request()->input('user_name');
$user->fcm = Request()->input('fcm');
$user->email = Request()->input('email');
$user->token = Request()->input('token');
$user->profile_pic = Request()->input('profile_pic');
$user->api_token = str_random(60);
$user->ref_ID = $this->incrementalHash(4);
$user->save();
}
I suggest you to use what Laravel provides to generate a random string. like: strtolower(str_random(4)) as mentioned by #kenken9999
However, Here is why I think it gave same result for you:
I executed your function multiple times and these are the outputs:
becpy
becqa
becqd
becqd
becqe
I think when you checked them they just happend to be same and when you did composer dump-autoload you happened to see a different output.
Let me know If I am wrong.
Did you call this function many times during a very short time? Then I believe the issue is microtime(). This function returns a string separated by a space. The first part is the fractional part of seconds, the second part is the integral part.
Thus, if the function is called during the same second, $now should be the same, based on which $result will not change.
Further, if the function is called during a short time (let's say several seconds), $now would be similar (1283846202 and 1283846203 for example). In this case, only the right part of $result would vary.
Related
I use a mutator to create a custom id for my records to make it look like this:
yyyy-mm-dd-{sequence}
The sequence looks like this
00001
00002
...
So it's 5 digits and is just a counter.
I have 2 problems
1) I don't know how to create a counter in my mutator, I can do a for loop but I don't now how to make an infinte loop that resets when it's tomorrow.
2) I honestly have no idea how to make it reset every day.
My mutator:
public function setFunctionalIdAttribute($id)
{
$date = Carbon::now()->format("Y-m-d");
// I take an extremely large number here because there will never be so much records in 1 day.
for ($counter = 0; $counter <= 100000000000; $counter++) {
$counter = str_pad($counter, 5, '0', STR_PAD_LEFT);
}
$today = Carbon::today();
$tomorrow = Carbon::tomorrow();
if ($today = $tomorrow) {
$counter = 0;
}
$this->attributes['functional_id'] = $date . "-" . $counter;
}
Its hard to say it but, in the nicest possible way, your counter loop doesn't really make any sense, I'm sorry! I'd recommend getting rid of that entirely, or at least read the PHP docs on str_pad.
You also have a conditional statement that checks "is today tomorrow". That to me is a big red flag that the logic, in general, isn't correct.
Let's think through an alternative. You're essentially counting the number of records in a day, to use that as the ID. I'd suggest an approach similar to this:
public function setFunctionalIdAttribute()
{
// 1. Count how many records there are from today
// 2. Make an ID that is this number + 1
// 3. If need be, string pad left with 0's
}
1. Count how many records there are from today
Laravel has a handy whereDate function – from the docs (search for whereDate)
$count = DB::table('users')
->whereDate('created_at', Carbon::today()->toDateString())
->count();
So if we had 3 records made today, $count would be 3.
2. Make an ID that is this number + 1
$count ++;
3. If need be, string pad left with 0's
The PHP docs on str_pad are pretty terrible, lets just cover the basics:
str_pad($input, $length, $pad_string, $pad_type);
$input is the string you are padding
$length is the final length of the string (this is why your for loop was totally unnecessary)
$pad_string if the string length is less than $length, fill up the remaining space with this
$pad_type as you rightly had, is an optional flag to pad left
Your $input is the $count, your $length is 5, judging from your example, $pad_string is "0", and we keep PAD_LEFT.
$id = str_pad($count, 5, "0", PAD_LEFT)
I can't remember how to set an attribute through a mutator so just copying your example (I hope that's correct!) we get:
public function setFunctionalIdAttribute()
{
$count = DB::table('users') // Remember to change this to the correct table name
->whereDate('created_at', Carbon::today()->toDateString())
->count();
$count ++;
$id = str_pad($count, 5, PAD_LEFT)
$this->attributes['functional_id'] = $id;
}
Remember to only do this on create, as we don't want to increment this ID on every save.
I don't know the purpose of your code, but this will allways set "functional_id" to something like "2019-01-23-100000000001", since you are using the $counter variable out of your loop.
Your loop is looping trough without doing anything. (And why the hell are you looping till such a high number if you are not expecting more than 100000 entries?!?)
What you need is the previous counter you have set, ether from DB or somewhere else, but like this your code is not going to work.
In that way you could perform some check like
if ($dateOfLastEntry != Carbon::now()->format('Y-m-d')) {$counter = 1}
otherwise set $counterOfLastEntry + 1
not using that scary for-loop you are using
str_pad performed at the end
Maybe you give us a little more information how this should work, for what you are going to use that counter, and where you are going to store this data.
I know it is a old question, but for everybody that needs something like it for days to come, I made something that resolve the question.
public function Counter()
{
$today = Carbon::today();
$today_files = Shipment::whereDate('created_at', $today)->count();
$counter= $today_files;
if ($today_files != 0) {
$counter++;
} else {
$counter = 1;
}
return counter;
}```
I have a thousand (for example, could be more) strings and for each string, there is a field associated with it which represents time interval.
For each one of the strings, I need to perform a task which takes the string as input and produces some output, every X minutes (X being the time interval mentioned above).
If it was a single value of time interval for all the strings, then I would set up a single cron job and that would suffice; but I have a different value of time interval for each of the strings.
So I'll set up a thousand or more cron jobs. That does not feel right. So what would be the best way to solve this problem?
You might want to look into using a library that already did this instead of re-inventing the wheel yourself.
https://packagist.org/packages/peppeocchi/php-cron-scheduler
But, if you'd really want to code it yourself you'll need to have "two variables" one with last executed which has to be read from a file or database in between execution cycles, one with interval and have cron call your script every second/minute
Take a look at the proof of concept code here. Untested but it should point you in to how it should work in theory.
class Job
{
protected $interval = 0;
protected $lastrun = 0;
protected $job = null;
protected $filename = null;
public function __construct($id, $interval,callable $job)
{
$this->interval = $interval;
$this->job = $job;
$this->filename = __DIR__.'/'.$id.'.job';
$this->lastrun = file_get_contents($this->filename) ? : 0;
}
public function attemptRun($time)
{
if($time - $this->lastrun >= $this->interval) {
$this->run($time);
}
}
protected function run($time)
{
file_put_contents($this->filename, $time);
$this->job();
}
}
$jobs = [
new Job('addition', 10, function() { $a = 1; $b = 2; $c = $a + $b;}),
new Job('subtraction', 20, function() { $a = 1; $b = 2; $c = $a - $b;}),
];
var $currentTime = time();
foreach($jobs as $job) {
$job->attemptRun($currentTime);
}
Cron job: Run it every minute.
Let's say you have 3 strings in the format string_value-execute after certain minutes-
some_string-5
some_string_2-10
some_string_3-15
So, execution time if we start from 0 will have a series like this-
0
5 (execute first)
10 (execute first as well as second string)
15 (execute first and third string)
20 (execute first as well as second string)
25 (execute only first)
30 (execute first,second,third)
Database part:
Have 2 tables.
First table- has a single column having current cron minute.
second table- Have column as string , interval duration , next_execution_time
Now, insert into first table whenever you run it.
Secondly, do a "Select * from table_name where next_execution_time = current_got_fetched_from_first_table".
Introduce new strings- When you are doing so, insert as first start interval = current_cron_time + interval_duration to execute.
P.S- When processing completes, you also need to update the second table with their respective next_execution_time.
I need to generate a random 4 character string on each form submission. I got this solution from here.
which is this.
function genTicketString() {
return substr(md5(uniqid(mt_rand(), true)), 0, 4);
}
add_shortcode('quoteticket', 'genTicketString');
But this mostly generates a similar ID! probably if I can add the date & time along with the 4 character, it will fix it.
So how can I add the data & the time to the generated string?
As a direct answer to your question
To generate a pseudo random string, you can use this function :
function getPseudoRandomString($length = 4) {
$base64Chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/';
$result = '';
for ($i = 0; $i < $length; ++$i) {
$result .= $base64Chars[mt_rand(0, strlen($base64Chars) - 1)];
}
return $result;
}
NOTE : This generates a pseudo random string, there is no way to be sure the string is unique.
To get a "more unique" string
first, you should use a longer string : 4 chars is really small : there are only 16 million possibilities with a set of 64 chars.
Then, If you want to add more unicity, you can concatenate a random generated string with the result of uniqid('', true) http://php.net/manual/en/function.uniqid.php
To be sure the string has never been generated
The only way to to be sure the string has never been generated is to save all generated strings in a database and when you generate a new string, you have to check if the string already exists in the database to generate a new one if needed.
The generator function will look like
function generateUniqueString()
do {
$string = generateString();
while (is_in_database($string));
save_in_database($string);
return $string;
}
I used this answer instead with little modifications
function genTicketString() {
$d=date ("d");
$m=date ("m");
$y=date ("Y");
$t=time();
$dmt=$d+$m+$y+$t;
$ran= rand(0,10000000);
$dmtran= $dmt+$ran;
$un= uniqid();
$dmtun = $dmt.$un;
$mdun = md5($dmtran.$un);
$sort=substr($mdun, 0, 6); // if you want sort length code.
$sort=strtoupper($sort);
return $sort;
}
add_shortcode('quoteticket', 'genTicketString');
You have a function that always inputs an interval (natural numbers in this case), this function returns a result, but is quite expensive on the processor, simulated by sleep in this example:
function calculate($start, $end) {
$result = 0;
for($x=$start;$x<=$end;$x++) {
$result++;
usleep(250000);
}
return $result;
}
In order to be more efficient there is an array of old results, that contains the interval used an the result of the function for that interval:
$oldResults = [
['s'=>1, 'e'=>2, 'r' => 1],
['s'=>2, 'e'=>6, 'r' => 4],
['s'=>4, 'e'=>7, 'r' => 3]
];
If I call calculate(1,10) the function should be able to calculate new intervals based on old results and accumulate them, In this particular case it should take the old result from 1 to 2 add that to the old result from 2 to 6 and do a new calculate(6,10) and add that too. Take in consideration that the function ignores the old saved interval from 4 to 7 since it was more convenient to use 2-6.
This is a visual representation of the problem:
Of course in this example, calculate() is quite simple and you can just find particular ways to solve this problem around it, but in the real code calculate() is complex and the only thing I know is that calculate(n0,n3)==calculate(n0,n1)+calculate(n1,n2)+calculate(n2,n3).
I cannot find a way to solve the reuse of the old data without using a bunch of IF and foreach, I'm sure there is a more elegant approach to solve this.
You can play with the code here.
Note: I'm using PHP but I can read JS, Pyton, C and similar languages.
if you are certain that calculate(n0,n3)==calculate(n0,n1)+calculate(n1,n2)+calculate(n2,n3), then it seems to me that one approach might simply be to establish a database cache.
you can pre-calculate each discrete interval, and store its result in a record.
$start = 0;
$end = 1000;
for($i=1;$i<=$end;$i++) {
$result = calculate($start, $i);
$sql = "INSERT INTO calculated_cache (start, end, result) VALUES ($start,$i,$result)";
// execute statement via whatever dbms api
$start++;
}
now whenever new requests come in, a database lookup should be significantly faster. note you may need to tinker with my boundary cases in this rough example.
function fetch_calculated_cache($start, $end) {
$sql = "
SELECT SUM(result)
FROM calculated_cache
WHERE (start BETWEEN $start AND $end)
AND (end BETWEEN $start AND $end)
";
$result = // whatever dbms api you chose
return $result;
}
there are a couple obvious considerations such as:
cache invalidation. how often will the results of your calculate function change? you'll need to repopulate the database then.
how many intervals do you want to store? in my example, I arbitrarily picked 1000
will you ever need to retrieve non-sequential interval results? you'll need to apply the above procedure in chunks.
i wrote this:
function findFittingFromCache($from, $to, $cache){
//length for measuring usefulnes of chunk from cache (now 0.1 means 10% percent of total length)
$totalLength = abs($to - $from);
$candidates = array_filter($cache, function($val) use ($from, $to, $totalLength){
$chunkLength = abs($val['e'] - $val['s']);
if($from <= $val['s'] && $to >= $val['e'] && ($chunkLength/$totalLength > 0.1)){
return true;
}
return false;
});
//sorting to have non-decremental values of $x['s']
usort($candidates, function($a, $b){ return $a['s'] - $b['s']; });
$flowCheck = $from;
$needToCompute = array();
foreach($candidates as $key => $val){
if($val['s'] < $flowCheck){
//already using something with this interval
unset($candidates[$key]);
} else {
if($val['s'] > $flowCheck){
//save what will be needed to compute
$needToCompute[] = array('s'=>$flowCheck, 'e'=>$val['s']);
}
//increase starting position for next loop
$flowCheck = $val['e'];
}
}
//rest needs to be computed as well
if($flowCheck < $to){
$needToCompute[] = array('s'=>$flowCheck, 'e'=>$to);
}
return array("computed"=>$candidates, "missing"=>$needToCompute);
}
It is function which returns you two arrays, one "computed" holds found already computed pieces, second "missing" holds gaps between them which must be computed yet.
inside function there is 0.1 threshold, which disqualifies chunks shorter than 10% of total searched length, you can rewrite function to send threshold as parameter, or ommit it completely.
i presume results will be stored and after computing added into cache ($oldResults), which might be of any form (for example database as Jeff Puckett suggested). Do not forget to add all computed chunks and whole seeked length into cache.
I am sorry but i can't find a way without cycles and ifs
Working demo:
link
hope that someone helps me understand this:
function getCache($key, $timeout = 30) {
$contents = false;
if (MEMCACHE <> 0) {
global $memcache;
$contents = $memcache->get($key);
}
if (empty($contents)) {
return false;
}
return $contents;
}
if($onlineFrnds = getCache($userid, 30)){
//do stuff
}
I didn't write this code, it's in code I am trying to modify (and so to understand).
What I can't explain is the call of setCache with 30 and why it is set again with $timeout = 30. Morevoer the $timeout is not used in the function.
What happens is that the if condition evaluates to true if 30 seconds are passed.
$timeout = 30 is a default parameter. This means if you don't provide it the parameter will use the value 30.
Here's a little demo to show you how it works:
function demo($timeout = 30) {
return $timeout;
}
echo demo(); // 30
echo demo(15); //15
It looks like the $timeout was meant to be used to expire the cache but was never used.
What you are seeing there is the definition of an optional parameter.
Let's see, example... ah, here we go (http://php.net/trim)
string trim ( string $str [, string $character_mask = " \t\n\r\0\x0B" ] )
Do you see there, how the $character_mask parameter is "defined" in the function signature? What that means is that, if you do not pass it a parameter there, then it will default to that.
Now, in your code, it's being set to 30 and 30 is the default. That's fine, it means that it's not dependent on the default (because it could, in theory, change, so it will always be 30 for this call).
Now, as for it not being used, that seems more like a design flaw in the code itself, or possible a Copy/Paste/Derp™, in which the function signature was copied from another function and then not edited. It makes no significant difference.