I'm trying to count how many numbers inside of a variable. Here is the regex that i use..
preg_match('/[^0-9]/', $password, $numbers, PREG_OFFSET_CAPTURE);
When I try to get all numbers one by one, I use:
print_r($this->filter->password_filter($data["user_password"]));
user_password is 123d4sd6789. Result is an empty array.
Array ( )
Well, you can easily do it using preg_split:
$temp = preg_split('/\d/', $password);
$numCount = count($temp) - 1;
Your regex is flawed, since you're trying to count the number of digits in a password, using:
/[^0-9]/
Won't cut it, perhpas you meant to write:
/[0-9]/
Because what you have now matches everything EXCEPT a number.
There are a great number of ways to do what you're trying to do, I've benchmarked 4 different approaches, and found that using regex is the fastest approach. Using the bundled pcre extension that PHP ships with, preg_split outperforms all other approaches ~70% of the time, 20% of the time, the loops are faster, though, and ~10% preg_match_all is fastest.
On codepad, who don't use the standard PCRE for some reason, preg_match_all doesn't work, nor did shuffle prove to be reliable, so I added a knuth method, and I decided to test the differences between /\d/ and /[0-9]/ in combination with preg_split instead. On codepad, regex is faster >95% of the time as a result.
In short: use preg_split + regex for the best results.
Anyway, here's the benchmark code. It may seem silly to put it all into a class, but really, it's the fair way to benchmark. The string that is processed is kept in memory, as are all the arrays that are used to time the functions, and compare speeds.
I'm not calling the test methods directly, either, but use a timeCall method instead, simply because I want the garbage collector to GC what needs to be GC'ed after each call. Anyway, it's not too difficult to figure this code out, and it's the results that matter
class Bench
{
/**
* #var string
*/
private $str = '123d4sd6789';
private $functions = array(
'regex' => null,
'regex2' => null,
'loop' => null,
'loop2' => null
);
private $random = null;
public function __construct()
{
$this->random = array_keys($this->functions);
if (!shuffle($this->random)) $this->knuth();
}
/**
* Knuth shuffle
*/
private function knuth()
{
for ($i=count($this->random)-1,$j=mt_rand(0,$i);$i>0;$j=mt_rand(0,--$i))
{
$temp = $this->random[$j];
$this->random[$j] = $this->random[$i];
$this->random[$i] = $temp;
}
}
/**
* Call all functions in random order, timing each function
* determine fastest approach, and echo results
* #param $randomize
* #return string
*/
public function test($randomize)
{
if ($randomize) if (!shuffle($this->random)) $this->knuth();
foreach($this->random as $func) $this->functions[$func] = $this->timeCall($func);
$fastest = array('f', 100000);
foreach($this->functions as $func => $time)
{
$fastest = $fastest[1] > $time ? array($func, $time) : $fastest;
echo 'Function ', $func, ' took ', $time, 'ms', PHP_EOL;
}
echo 'Fastest approach: ', $fastest[0], ' (', $fastest[1], 'ms)', PHP_EOL;
return $fastest[0];
}
/**
* Time function call
* #param string $func
* #return float mixed
*/
private function timeCall($func)
{
echo $func, PHP_EOL;
$start = microtime(true);
$this->{$func}();
return (microtime(true) - $start);
}
/**
* Count digits in string using preg_split
* #return int
*/
private function regex()
{
return count(preg_split('/\d/', $this->str)) - 1;
}
/**
* count digits in string using str_split + is_numeric + loop
* #return int
*/
private function loop()
{
$chars = str_split($this->str);
$counter = 0;
foreach($chars as $char) if (is_numeric($char)) ++$counter;
return $counter;
}
/**
* count digits by iterating over string, using is_numeric
* #return int
*/
private function loop2()
{
for($i=0,$j=strlen($this->str),$counter=0;$i<$j;++$i) if (is_numeric($this->str{$i})) ++$counter;
return $counter;
}
/**
* use preg_split + [0-9] instead of \d
* #return int
*/
private function regex2()
{
return count(preg_split('/[0-9]/', $this->str)) - 1;
if (preg_match_all('/[0-9]/',$this->str, $matches)) return count($matches);
return 0;
}
}
$benchmark = new Bench();
$totals = array();
for($i=0;$i<10;++$i)
{
$func = $benchmark->test($i);
if (!isset($totals[$func])) $totals[$func] = 0;
++$totals[$func];
if ($i < 9) echo PHP_EOL, '---------------------------------------------', PHP_EOL;
}
var_dump($totals);
Here's the codepad I set up
Do you really want a regex?
$arr1 = str_split($password);
$counter=0;
foreach($arr1 as $v){
if(is_numeric($v))$counter++;
}
Use preg_match_all to select all the numbers:
$reg = '/[0-9]/';
$string = '123d4sd6789';
preg_match_all($reg, $string, $out);
echo (count($out[0]));
Related
From a database I am getting a text where the function htmlentities() was applied four times. Sample text:
specials &amp; workshops
In order to decode this text I have to do the following:
$out = html_entity_decode(html_entity_decode(html_entity_decode(html_entity_decode("specials &amp;amp; workshops"))));
Result:
specials & workshops
Is there a natural way in PHP to write this more efficient?
I like to do it recursively in such a way that I do not need to know how many entities to match.
$string = 'specials &amp;amp; workshops';
$entity = '/&/';
function recurseHTMLDecode($str, $entity) {
preg_match($entity, $str, $matches);
echo count($matches);
if(1 == count($matches)) {
$str = html_entity_decode($str);
$str = recurseHTMLDecode($str, $entity);
return $str;
} else {
return $str;
}
}
var_dump(recurseHTMLDecode($string, $entity));
This returns:
11110string(20) "specials & workshops"
Here is the EXAMPLE
This could be improved by adding a whitelist of entities to the function so you would not have to specify the entity when calling, just loop through the whitelist. This would solve the issue of having more than one entity in a string. It could be quite complex.
Why not declare a function to do so?
$in = "specials &amp; workshops";
$decode = function($in) {
foreach(range(1,4) as $x) $in = html_entity_decode($in); return $in; };
function decode($in) {
foreach(range(1,4) as $x)
$in = html_entity_decode($in);
return $in;
}
// inline
$out = $decode($in);
// traditional
$out = decode($in);
According to the recursive idea of #JayBlanchard I have no created the following - really like it:
/**
* Apply a function to a certain input multiple times.
*
* #param $input: The input variable:
* #param callable $func: The function to call.
* #param int $times: How often the function should be called. -1 for deep call (unknown number of calls required). CAUTION: If output always changes this results in an endless loop.
* #return mixed
*/
function recapply($input,callable $func,int $times) {
if($times > 1) {
return recapply($func($input),$func,$times - 1);
} else if($times == -1) {
$res = $func($input);
if($res === $input) {
return $input;
} else {
return recapply($res,$func,-1);
}
}
return $func($input);
}
Working example call:
echo recapply("specials &amp; workshops","html_entity_decode",4);
I have the some code which looks something like this (I've simplified it):
function process_something($a){
foreach($a as $b){
// Some logic here
return $something;
}
}
$input=[]; // An array of some kind
echo process_something($input);
I expect that final line to echo what the loops have returned but I get nothing. Maybe the above code will not work. I just put it in for illustration. I have a lot of nested loops working together to return various things.
If I have the loops echo data out, it works. However, I need this function to just return the end result to me for further processing, rather than echoing out to the user.
How do I proceed?
In this case this loop will only run once, because return jumps out of a function on the first occurrence.
It should be more like:
function process_something($a){
foreach($a as $b){
$something = 'Some math or other logic here';
}
return $something;
}
$input=[]; // An array of some kind
echo process_something($input);
Please post your code, we will try to figure out what's wrong with it.
This is the perfect case for a generator, a concept that I rarely seen used.
function process_something($a){
foreach($a as $b){
// Some logic here
yield $something;
}
}
$input=[]; // An array of some kind
/**
* The generator returns an Iterator instance
* so you'd need to loop over it
*/
foreach(process_something($input) as $item){
// do stuff here
// echo $item
// echo $item->something
// your call
}
The major advantage here is that you can "return without actually returning", you're yielding a value and the generator continues on with it's work.
Not to mention it's very memory efficient, altough 99% of the times memory is not an issue.
AND, this is the nice part, you can apply specific logic for each of the items in the $input without needing to do some weird hack-ish function.
Alternatives
function process_something($a){
$return = [];
foreach($a as $b){
// Some logic here
$return[] = $something;
}
return $return;
}
The only reason this answer isn't a comment is that it's very rare that I see a question that would legitimately benefit from a generator. This is one of them.
More on generators:
http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html
https://scotch.io/tutorials/understanding-php-generators
I did get you properly . But I think you are looking for the last value of the array .
to do that just do it :
function process_something($a){
$b = array_reverse ($a);
return $b[0];
}
class StrHelper
{
/**
* Check for $string starts with $query
* #param $string
* #param $query
* #return bool
*/
public static function startsWith($string,$query){
return substr($string, 0, strlen($query)) === $query;
}
}
class ModelSearchHelper
{
/**
* filter where $key of $data arr startsWith $modelName
* #param string $modelName
* #param array $data
* #return array
*/
public static function searchFor(string $modelName,array $data){
// this code do same
// $out=[];
// foreach ($data as $key=>$value){
// if(StrHelper::startsWith($key,$modelName)){
// $out[$key]=$value;
// }
// }
// return $out;
// of this
return array_filter($data,function ($value,$key) use ($modelName){
return StrHelper::startsWith($key,$modelName);
},ARRAY_FILTER_USE_BOTH );
}
}
$test=[
'limit'=>10,
'offset'=>5,
'customer.name'=>'Fox',
'customer.address.street'=>'forest st.'
];
//filter where $key of $model startsWith $modelName
var_dump(ModelSearchHelper::searchFor('customer',$test));
result:
array(2) {
["customer.name"]=>
string(3) "Fox"
["customer.address.street"]=>
string(10) "forest st."
}
Good morning,
I need to truncate a string with a specific delimiter character.
For example with that string:
myString = 'customname_489494984';
I would like to strip automatically every string parts after "_"
(in this case "489494984").
Is there a function in order to truncate the string part after a specific
delimiter?
Many thanks!
François
You can also use strstr() which finds the first occurrence of a string:
$myString= "customname_489494984";
echo strstr($myString, '_', true);
Here's my reference: http://php.net/manual/en/function.strstr.php
Use a simple combo of substr and strpos:
$myString = 'customname_489494984';
echo substr($myString, 0, strpos($myString, '_'));
BONUS - Wrap it in a custom function:
function truncateStringAfter($string, $delim)
{
return substr($string, 0, strpos($string, $delim));
}
echo truncateStringAfter('customname_489494984', '_');
Try this,It will remove anything after "_".
$string= 'customname_489494984';
$string=substr($string, 0, strrpos($string, '_'));
The problem with other straightforward approaches is that if the String doesn't contain “_” then an empty String will be returned. Here is a small StringUtils class (part of a larger one) that does that with beforeFirst static method in a multibyte safe way.
var_dump(StringUtils::beforeFirst("customname_489494984", "_"));
class StringUtils
{
/**
* Returns the part of a string <b>before the first</b> occurrence of the string to search for.
* #param <b>$string</b> The string to be searched
* #param <b>$search</b> The string to search for
* #param <b>$caseSensitive boolean :optional</b> Defines if the search will be case sensitive. By default true.
* #return string
*/
public static function beforeFirst($string,$search,$caseSensitive = true)
{
$firstIndex = self::firstIndexOf($string, $search,$caseSensitive);
return $firstIndex == 0 ? $string : self::substring($string, 0 , $firstIndex);
}
/**
* Returns a part of the string from a character and for as many characters as provided
* #param <b>$string</b> The string to retrieve the part from
* #param <b>$start</b> The index of the first character (0 for the first one)
* #param <b>$length</b> The length of the part the will be extracted from the string
* #return string
*/
public static function substring($string,$start,$length = null)
{
return ( $length == null ) ? ( mb_substr($string, $start) ) : ( $length == 0 ? "" : mb_substr($string, $start , $length) );
}
/**
* Return the index of <b>the first occurance</b> of a part of a string to the string
* #param <b>$string</b> The string to be searched
* #param <b>$search</b> The string to search for
* #param <b>$caseSensitive boolean :optional</b> Defines if the search will be case sensitive. By default true.
* #return number
*/
public static function firstIndexOf($string,$search,$caseSensitive = true)
{
return $caseSensitive ? mb_strpos($string, $search) : mb_stripos($string, $search);
}
/**
* Returns how many characters the string is
* #param <b>$string</b> The string
* #return number
*/
public static function length($string)
{
return mb_strlen($string);
}
}
I am developing a event organization website. Here when the user registers for an event he will be given a unique random number(10 digit), which we use to generate a barcode and mail it to him. Now,
I want to make the number unique for each registered event.
And also random
One solution is to grab all the random numbers in an array and generate a random number using Php rand(1000000000, 9999999999) and loop through and check all the values. Grab the first value that doesn't equal to any of the values in the array and add it to the database.
But I am thinking that there might be a better solution to this. Any suggestion?
You can use php's uniqid() function to generate a unique ID based on the microtime (current time in microseconds)
Example:
<?php
echo uniqid();
?>
Output:
56c3096338cdb
Your logic isn't technically faulty. However, if your application attracts lots of users, fetching all of the random numbers may well become unnecessarily expensive, in terms of resources and computation time.
I would suggest another approach, where you generate a random number and then check it against the database.
function generateBarcodeNumber() {
$number = mt_rand(1000000000, 9999999999); // better than rand()
// call the same function if the barcode exists already
if (barcodeNumberExists($number)) {
return generateBarcodeNumber();
}
// otherwise, it's valid and can be used
return $number;
}
function barcodeNumberExists($number) {
// query the database and return a boolean
// for instance, it might look like this in Laravel
return User::whereBarcodeNumber($number)->exists();
}
This is good:
do {
$refrence_id = mt_rand( 1000000000, 9999999999 );
} while ( DB::table( 'transations' )->where( 'RefrenceID', $refrence_id )->exists() );
To avoid the problem of having to check to see if a matching code exists every time a new one is created, I just catch MySQL's duplicate record exception (error code 1062). If that error is caught, I just call the function again until the save is successful. That way, it only has to generate a new code if it collides with an existing one. Runs a lot faster -- but obviously gets a bit slower as your number of users approaches the number of possible barcodes.
function generateBarcode($user_id) {
try {
$user = User::find($user_id);
$user->barcode = mt_rand(1000000000, 9999999999);
$user->save();
} catch (Exception $e) {
$error_info = $e->errorInfo;
if($error_info[1] == 1062) {
generateBarcode($user_id);
} else {
// Only logs when an error other than duplicate happens
Log::error($e);
}
}
}
So just loop through all the users you want to assign a code to:
foreach(User::all() as $user) {
generateBarcode($user->id);
}
You could also add some logic to escape the function loop if a maximum number of attempts are made, but I've never bothered because collisions are unlikely.
Looping through the array won't be that efficient. If your database becomes too large then it slow down the entire process and also there might be a rare situation when 2 threads are looping through the array for the same random number and it will be found available and return same number to both the tickets.
So instead of looping through the array you can set the 10 digit registration id as primary key and instead of looping through the array you can insert the registration details along with randomly generated number, if the database insert operation is successful you can return the registration id but if not then regenerate the random number and insert.
Alternate solution which will be more effective
Instead of 10 digit random numbers you can use timestamp to generate a 10 digit unique registration number and to make it random you can randomize the first 2 or 3 digits of the timestamp
One Solution could be like this:
use Illuminate\Support\Facades\Validator;
private function genUserCode(){
$this->user_code = [
'user_code' => mt_rand(1000000000,9999999999)
];
$rules = ['user_code' => 'unique:users'];
$validate = Validator::make($this->user_code, $rules)->passes();
return $validate ? $this->user_code['user_code'] : $this->genUserCode();
}
Its generating a random number between 1000000000 and 9999999999. After that, it validates the number against the table. If true then it returns the number, otherwise runs the function again.
I made something like this
/**
* Generate unique shipment ID
*
* #param int $length
*
* #return string
*/
function generateShipmentId($length)
{
$number = '';
do {
for ($i=$length; $i--; $i>0) {
$number .= mt_rand(0,9);
}
} while ( !empty(DB::table('shipments')->where('id', $number)->first(['id'])) );
return $number;
}
<?php
declare(strict_types=1);
namespace App\Helpers;
use App\Exceptions\GeneratorException;
class GeneratorHelper
{
public static $limitIterations = 100000;
/**
* #param string $column
* #param string $modelClass
* #return string
* #throws GeneratorException
*/
public static function generateID(string $modelClass, string $column): string
{
return self::run(
$modelClass,
$column,
self::IDGenerator(),
'Generation id is failed. The loop limit exceeds ' . self::$limitIterations
);
}
/**
* #param string $modelClass
* #param string $column
* #param \Generator $generator
* #param string $exceptionMessage
* #param array $whereParams
* #return string
* #throws GeneratorException
*/
protected static function run(string $modelClass, string $column, \Generator $generator, string $exceptionMessage, array $whereParams = []): string
{
try {
foreach ($generator as $id) {
$query = $modelClass::where([$column => $id]);
foreach ($whereParams as $param) {
$query->where(...$param);
}
if (!$query->first()) {
return $id;
}
}
} catch (\Throwable $e) {
$exceptionMessage = $e->getMessage();
}
throw new GeneratorException($exceptionMessage);
}
protected static function IDGenerator(): ?\Generator
{
for ($i = 1; $i <= self::$limitIterations; $i++) {
yield (string)random_int(1000000000, 9999999999);
}
return null;
}
}
sample usage
$card->number = GeneratorHelper::generateID(Card::class, 'number');
for me, I prefer using MySQL way, because when you have a large amount of data in your DB, you will have too much quires to check your number uniqueness,
for example, a code like this:
do {
$code = random_int(100000, 99999999);
}
while (AgentOrder::where("order_number", "=", $code)->exists());
so , this "do while" loop would be excueted too many times.
to avoid that, you can use MySQL way like:
private function getUniqueCodeNumber()
{
$value = AgentOrder::query()->selectRaw('FLOOR(1000000 + RAND() * 10000000) AS generatedCode')
->whereRaw("'generatedCode' NOT IN (SELECT order_number FROM agent_orders WHERE agent_orders.order_number IS NOT NULL)")
->limit(1)->first();
if ($value == null) return 100000000;
$value = (int)$value->generatedCode;
return $value;
}
this answer is inspired from this answer.
Helper (app/Helpers/Constants.php)
<?php
namespace App\Helper;
class Constants
{
public static function getUniqueId($model){
$id = mt_rand(1000000000, 9999999999);
if($model->find($id))
return self::getUniqueId($model);
return $id;
}
}
?>
Model (app/Models/User.php)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public static function boot(){
parent::boot();
$creationCallback = function ($model) {
if (empty($model->{$model->getKeyName()}))
$model->{$model->getKeyName()} = Constants::getUniqueId(new self());
};
static::creating($creationCallback);
}
}
?>
Explanation: Instead of calling the getUniqueId method in every controller. You can write it inside model.
From the #Joel Hinz answer :
public function set_number() {
$number = mt_rand(1000000000, 9999999999);
return User::where('number', $number)->exists() ? $this->set_number() : $number;
}
Hope that helped.
I have a Codeigniter controller which takes a full URL as the first argument, but the passed URL inside my controller only is only showing http:
public function mydata($link)
{
echo $link; //then it show only http: rather than the full url http://abc.com
}
How can i solve this issue?
if you want to pass url as parameters then use
urlencode(base64_encode($str))
ie:
$url=urlencode(base64_encode('http://stackoverflow.com/questions/9585034'));
echo $url
result:
aHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85NTg1MDM0
then you call:
http://example.com/mydata/aHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85NTg1MDM0
and in your controller
public function mydata($link)
{
$link=base64_decode(urldecode($link));
...
...
...
you have an encoder/decoder here:
http://www.base64decode.org/
In Codeigniter controllers, each method argument comes from the URL separated by a / slash. http://example.com
There are a few different ways to piece together the the arguments into one string:
public function mydata($link)
{
// URL: http://example.com/mysite/mydata/many/unknown/arguments
// Ways to get the string "many/unknown/arguments"
echo implode('/', func_get_args());
echo ltrim($this->uri->uri_string(), '/');
}
However:
In your case, the double slash // may be lost using either of those methods because it will be condensed to one in the URL. In fact, I'm surprised that a URL like:
http://example.com/mydata/http://abc.com
...didn't trigger Codeigniter's "The URI contains disallowed chatacters" error. I'd suggest you use query strings for this task to avoid all these problems:
http://example.com/mydata/?url=http://abc.com
public function mydata()
{
$link = $this->input->get('url');
echo $link;
}
Aside from the issue of whether you should be passing a URL in a URL think about how you are passing it:
example.com/theparameter/
but your URL will actually look like
example.com/http://..../
See where you're going wrong yet? The CodeIgniter framework takes the parameter out of the URL, delimited by slashes. So your function is working exactly as it should.
If this is how you must do it then URL encode your parameter before passing it.
You can try this. It worked fr me.
"encode" the value before passing
$value = str_replace('=', '-', str_replace('/', '_', base64_encode($album)));
"decode" the value after receiving
$value = base64_decode(str_replace('-', '=', str_replace('_', '/', $value)));
reference: https://forum.codeigniter.com/printthread.php?tid=40607
I did like #user72740's until I discovered that it can still produce characters not permitted by CI like %.
What I ended up doing is converting the segment string into a hex, them back.
So I created a MY_URI that extended CI_URI and added these methods:
/**
* Segmentize
*
* Makes URI segments, CI "segment proof"
* Removes dots and forwardslash leaving ONLY hex characters
* Allows to pass "anything" as a CI URI segment and coresponding function param
*
* #access public
* #return string
*/
public function segmentize($segment){
if(empty($segment)){
return '';
}
return bin2hex($segment);
}
/**
* Desegmentize
*
* #access public
* #return string
*/
public function desegmentize($segment){
if(empty($segment)){
return '';
}
return $this->hex2bin($segment);
}
/**
* hex2bin
*
* PHP 5.3 version of 5.4 native hex2bin
*
* #access public
* #return string
*/
public function hex2bin($hex) {
$n = strlen($hex);
$bin = '';
$i = 0;
while($i < $n){
$a = substr($hex, $i, 2);
$c = pack('H*', $a);
if ($i == 0){
$bin = $c;
}
else {
$bin .= $c;
}
$i += 2;
}
return $bin;
}
Then used $this->uri->segmentize($url) to create the segment string and
$this->uri->desegmentize($this->input->post('url', true)) to get it back into readable format.
Thus
https://www.example.com/somewhere/over/the/rainbow
becomes
68747470733a2f2f7777772e6d79736974652e636f6d2f736f6d6577686572652f6f7665722f7468652f7261696e626f77
and back.
I am sure there is a better way, like a base_convert() implementation, because this way the string can get arbitrarily long. But now I dont have to worry about = signs and padding, etc.