I am working on a Webinterface that provides the same function like poEdit.
I want to walk trough all .php files in a specified folder and search every line for a translation. For this I would like to use regular expression searching the actual line in the php file and return the translation-text-parameter and the domain-parameter.
My function looks like this:
__('This is my translation', 'domain');
But because for the domain-parameter I defined a default, the function __() can also be called like this:
__('this is my translation');
Now in PHP i tried to use the Function preg_match_all() but i can't gent my regex together.
Here is an example of a possible line in the script and the output array I would like to receive with the preg_match_all() function:
echo __('Hello World'); echo __('Some domain specific translation', 'mydomain');
Array output:
Array
(
[0] => Array
(
[0] => Hello World
)
[1] => Array
(
[0] => Some domain specific translation.
[1] => mydomain
)
)
Can anyone help me out with the Regex and the preg_math_all() flags?
Thank you guys.
Something like this should work. Array shift needed, because zero element will always contain full match, there is no flag to exclude it AFAIK.
if(preg_match_all('/__\(\s*\'((?:[^\']|(?<=\\\)\')+)\'(?:\s*,\s*\'((?:[^\']|(?<=\\\)\')+)\')?\s*\)/us', $data, $result)) {
foreach ($result as &$item) {
array_shift($item);
}
unset($item);
var_dump($result);
}
It finds correctly calls like these __('lorem \' ipsum', 'my\'domain'). It would fail on __('lorem \\') though.
The regex you would need for this is considerably complex.
__\(\s*(['"])((?:(?!(?<!\\)\1).)+)\1(?:,\s*(['"])((?:(?!(?<!\\)\3).)+)\3)?\s*\)
Matches would be in groups 2 and 4, for example
__('This is my translation', 'domain');
would produce these groups:
'
This is my translation
'
domain
and this
__('This is my \'translation\'', "domain");
would produce these groups:
'
This is my \'translation\'
"
domain
Related
I have a list of domains (array)
sub1.dom1.tld1
sub2.dom2.tld2
sub1.sub2.dom1.tld1
sub3.dom1.tld3
I want to achieve the following:
dom1.tld1
-> sub1.dom1.tld1
-> sub2.dom1.tld1
--> sub1.sub2.dom1.tld1
dom2.tld2
-> sub2.dom2.tld2
dom1.tld3
-> sub3.dom1.tld3
I have tried to adapt this, but it doesn't really fit:
How to alphabetically sort a php array after a certain character in a string
I would appreciate any kind of help.
I've had to attack a similar headache before. In the short term I flip the order of the domain components and use a hidden sorting column in a table/view:
$sortstring = implode('.',array_reverse(explode('.', $domain)));
In the long term I saved the reverse format of the domain records before saving changes to the DB into a computed field/column so that it didn't have to be re-computed every time the domain list is viewed.
If you don't want that sub-domain, just remove the last element of the array after the flip....
You can proceed like this:
$array=array(
'sub1.dom1.tld1',
'sub2.dom2.tld2',
'sub1.sub2.dom1.tld1',
'sub2.sub2.dom1.tld1',
'sub3.sub2.dom1.tld1',
'sub3.dom1.tld3');
function cmp($a,$b){
$a=array_reverse(explode('.',$a));
$b=array_reverse(explode('.',$b));
$ca=count($a);
$cb=count($b);
$string='';;
for($i=0,$c=min($ca,$cb);$i<$c;$i++){
$result=strnatcmp($a[$i],$b[$i]);
if($result!==0) return $result;
}
return $result;
}
usort($array,'cmp');
print_r($array);
and the output is:
Array
(
[0] => sub1.dom1.tld1
[1] => sub1.sub2.dom1.tld1
[2] => sub2.sub2.dom1.tld1
[3] => sub3.sub2.dom1.tld1
[4] => sub2.dom2.tld2
[5] => sub3.dom1.tld3
)
Here is an approach similar to #Elementary answer combine to #CBO one:
$domains = [
'sub.bbb.com',
'www.aaa.com',
'*.zzz.com',
'aaa.com',
'*.sub.bbb.com',
'zzz.com',
'beta.bbb.com',
'bbb.com',
'aaa.fr',
];
// #see https://stackoverflow.com/a/61461912/1731473
$computeDomainToSort = static function (string $domain): string {
return \implode(
'.',
array_reverse(
explode('.', $domain,
// Keep the base domain.tld collapsed for comparison.
substr_count($domain, '.')
)
)
);
};
\usort($this->domains, static function (string $domain1, string $domain2) use ($computeDomainToSort): int {
$domain1 = $computeDomainToSort($domain1);
$domain2 = $computeDomainToSort($domain2);
return strnatcmp($domain1, $domain2);
});
That way, given domains will be sorted like this:
aaa.com
www.aaa.com
aaa.fr
bbb.com
beta.bbb.com
sub.bbb.com
*.sub.bbb.com
zzz.com
*.zzz.com
The main difference is on the $computeDomainToSort lambda function, where I keep the base domain.tld onto one piece to have a more natural sorting.
I'm working on an issue where users (truck drivers in this case) use SMS to send in information about work status. I want to keep the keying simple as not all users have smart phones so I have adopted some simple short codes for their input. Here are some examples and their meanings:
P#123456-3 (This is for picking up load 123456-3)
D#456789-1 (For the dropping of load 456789-1)
L#345678-9 (Load 345678-9 is going to be late)
This is pretty simple but users (and truck drivers) being what they are will key the updates in somewhat deviant manners such as:
#D 456789-1
D# 456789 - 1
D#.456789-1 This load looks wet to me do weneed to cancelthis order
You can pretty much come up with a dozen other permutations and it's not hard for me to catch and fix those that I can imagine.
I mostly use regular expressions to test the input against all my imagined "bad" patterns and then extract what I assume are the good parts, reassembling them into the correct order.
It's the new errors that cause me problems so I got to wondering if there was a more generic method where I can pass a "pattern" and a "message" to a function that would do it's best to turn the "message" into something matching the "pattern".
My searches have not found anything that really fits what I'm trying to do and I'm not even sure if there is a good general way to do this. I happen to be using PHP for this implementation but any type of example should help. Do any of you have a method?
If the user has problems with your software, fix the software, not the user!
The problem arises because your format looks unnecessary complicated. Why do you need the hash in the first place? How about simplifying it down to the following:
operation-code maybe-space load-number maybe-space and comment
Operation codes are assigned to different phone keys, so that J, K and L mean the same thing. Load-numbers can be sent as digits and as letters as well, e.g. agja means 2452. It's hard for the user to make a mistake using this format.
Here's some code to illustrate this approach:
function parse($msg) {
$codes = array(
3 => 'DROP',
5 => 'LOAD',
// etc
);
preg_match('~(\S)\s*(\S+)(\s+.+)?~', $msg, $m);
if(!$m)
return null; // cannot parse
$a = '.,"?!abcdefghijklmnopqrstuvwxyz';
$d = '1111122233344455566677777888999';
return array(
'opcode' => $codes[strtr($m[1], $a, $d)],
'load' => intval(strtr($m[2], $a, $d)),
'comment' => isset($m[3]) ? trim($m[3]) : ''
);
}
print_r(parse(' j ww03 This load looks wet to me'));
//[opcode] => LOAD
//[load] => 9903
//[comment] => This load looks wet to me
print_r(parse('dxx0123'));
//[opcode] => DROP
//[load] => 990123
//[comment] =>
Try something like this:
function parse($input) {
// Clean up your input: 'D#.456789 - 1 foo bar' to 'D 456789 1 foo far'
$clean = trim(preg_replace('/\W+/', ' ', $input));
// Take first 3 words.
list($status, $loadId1, $loadId2) = explode(' ', $clean);
// Glue back your load ID to '456789-1'
$loadId = $loadId1 . '-' . $loadId2;
return compact('status', 'loadId');
}
Example:
$inputs = array(
'P#123456-3',
'#D 456789-1',
'D# 456789 - 1',
'D#.456789-1 This load looks wet to me do weneed to cancelthis order',
);
echo '<pre>';
foreach ($inputs as $s) {
print_r(parse($s));
}
Output:
Array
(
[status] => P
[loadId] => 123456-3
)
Array
(
[status] => D
[loadId] => 456789-1
)
Array
(
[status] => D
[loadId] => 456789-1
)
Array
(
[status] => D
[loadId] => 456789-1
)
First, remove stuff that shouldn't be there:
$str = preg_replace('/[^PDL\d-]/i', '', $str);
That gives you the following normalised results:
D456789-1
D456789-1
D456789-1ldlddld
Then, attempt to match the data you want:
if (preg_match('/^([PDL])(\d+-\d)/i', $str, $match)) {
$code = $match[1];
$load = $match[2];
} else {
// uh oh, something wrong with the format!
}
Something like
/^[#\s]*([PDL])[#\s]*(\d+[\s-]+\d)/
or to be even more relaxed,
/^[^\d]*([PDL])[^\d]*(\d+)[^\d]+(\d)/
would get you what you want. But I'd prefer HamZa's comment as a solution: throw it back and tell them to get their act together :)
I have an array of many banned string and i have a small string contains one keywords, too and i want to write a function in php like this:
function is_ban($keyword,$bannedList) {
}
where $keyword is small string and $bannedList is an array like
Array
(
[0] => php
[1] => html
[2] => java
[3] => css
[....]
)
The function check keyword in banned list and return true or false.
function is_ban($keyword,$bannedList) {
return in_array($keyword, $bannedList);
}
This is my first reply on a php related question. As others have said, if you have a precisely defined array of banned words, and you have already taken the time to get the word $keyword from the user, then by all means just use PHP's native function in_array(). You may however need to do the following:
if(in_array(strtolower($keyword), $bannedList)){ //return true }
Just make sure of course that your $bannedList array is all lowercase as well. If however you need to do pattern matches inside longer strings, then you'll need to resort to regular expressions.
This is a simple way to define your function
function is_ban($keyword,$bannedList)
{
return in_array($keyword, $bannedList);
}
I am doing a very small online store application in PHP. So I have an array of maps in PHP. I want to search for a string (a product) in the array. I looked at array_search in PHP and it seems that it only looks for exact match. Do you guys know a better way to do this functionality? Since this is a very small part of what I am actually doing, I was hoping that there was something built in. Any ideas?
Thanks!
EDIT: The array contains "products" in this format:
[6] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => 2000-YM
)
[Name] => Team Swim School T-Shirt
[size] => YM
[price] => 15
[group] => Team Clothing
[id] => 2000-YM
)
[7] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => 3000-YS
)
[Name] => Youth Track Jacket
[size] => YS
[price] => 55
[group] => Team Clothing
[id] => 3000-YS
)
So I was wondering I can do a search such as "Team" and it would return me first item seen here. I am basing the search on the Name (again this is just something small). I understand that I can find the exact string, I am just stuck on the "best results" if it cannot find the exact item. Efficiency is nice but not required since I only have about 50 items so even if I use a "slow" algorithm it won't take much time.
array_filter lets you specify a custom function to do the searching. In your case, a simple function that uses strpos() to check if your search string is present:
function my_search($haystack) {
$needle = 'value to search for';
return(strpos($haystack, $needle)); // or stripos() if you want case-insensitive searching.
}
$matches = array_filter($your_array, 'my_search');
Alternatively, you could use an anonymous function to help prevent namespace contamination:
$matches = array_filter($your_array, function ($haystack) use ($needle) {
return(strpos($haystack, $needle));
});
foreach($array as $item){
if(strpos($item,"mysearchword")!== false){
echo 'found';
}
}
or you can use preg_match for more flexible search instead of strpos.
I think Marc B's answer was a good starting point but for me it had some problems. Such as you have to know what the Needle is at "compile time" because you can't dynamically change that value. also if the needle appeared at the start of the string element it would act like it's not there at all. so after a little experimenting I manged to come up with a way around both problems. so you don't have to create a new function for every different needle your going to want to use anymore.
function my_search($haystack)
{
global $needle;
if( strpos($haystack, $needle) === false) {return false;} else {return true;}
}
and it would be called like this:
$needle="item to search for";
$matches = array_filter($my_array, 'my_search');
and being as needle is now accessible in the same scope that the rest of the code is you can set needle to any other string variable you wanted, including user input.
Unfortunately, search is one of the more difficult things to do in computer science. If you build for search based on literal string matches or regular expressions (regex), you may find that you'll be unhappy with the relevance of the results that are returned.
If you're interested in rolling up your sleeves and getting a little dirty with a more sophisticated solution, I'd try Zend's Lucene implementation ( http://framework.zend.com/manual/en/zend.search.lucene.html ). I've implemented a search on a site with it. It took a few days, but the results were MUCH better than the 15 minute solution of literal string matching.
PS. Here's an example: http://devzone.zend.com/article/91
I have same Issue but i have created i function to search in array by passing the array, key and value.
public function searchinarr($array, $key, $value)
{
$results = array();
for($i=0;$i<count($array);$i++)
{
foreach($array[$i] as $k=>$val)
{
if($k==$key)
{
if(strpos($val,$value)!== false)
{
$results[] = $array[$i];
}
}
}
}
return $results;
}
I have a group of text based rules that are structured like this:
Rule 1: Do [XXX] when [PN] greater than [N]
Rule 2: Get [PRD ..] and add [X.XX]
To go with this is an array of data that translates each grouped code into a CSS class ID (for jQuery).
I also have an array of translations from [code] to ID stored in a simple structured array, like the following example:
$translate = array(
'XXX' => 'gen-string-input',
'PN' => 'gen-positivenumber-input',
'N' => 'gen-number-input'
);
It is important that the following can be achieved:
I need to replace each instance of [code] with a span tag that is structured like this:
<span class="[classname]" unique="[hash]" offset="[offset]">[CODE]</span>
This is assuming that the fields are
classname is the result of the $translate array
hash is an md5 hash that is static for each rule
offset is the position of the field in the string (e.g. in the first example, field [XXX] is at position 0, [PN] at position 1 and so on).
Based on this information, I would expect to achieve the following output for Rule 1:
<p>
Do <span class="gen-string-input"
unique="[md5]"
offset="0">[XXX]</span>
when <span class="gen-positivenumber-input"
unique="[md5]"
offset="1">[PN]</span>
greater than <span class="gen-number-input"
unique="[md5]"
offset="2">[N]</span>
</p>
Any help is greatly appreciated, I am currently using str_replace to try and achieve this but it is just not good enough.
Ok, actually what you need is preg_replace_callback. See the recursive callback examples.
Iterate through the translate array, replacing the keys in the string with the values
$string = '[CODE]';
$translate = array('classname' => 'oddRow', 'hash' => 'abcdef');
foreach($translate AS $key=>$value)
{
$string = str_ireplace('[' . $key . ']', $value, $string);
}