Array merging: Keeping literal variables - php

Let me just jump directly into my problem.
The Build-up
I have some simple language files which just return an associative array containing the language strings (This is Laravel if that's of any help). Don't worry about the variable, that's just for demonstrational purposes.
lang/en/common.php:
<?php return [
"yes"=>"Yes",
"no"=>"No",
"hello"=>"Hello {$name}!",
"newstring"=>"This string does not exist in the other language file",
"random_number"=>"Random number: ".rand(1,10)
];
lang/da/common.php:
<?php return [
"yes"=>"Ja",
"no"=>"Nej",
"hello"=>"Hej {$name}!"
];
Now, as you can see, the index newstring doesn't exist in the danish language file. Instead of having to remember to add all the indexes all the language files instead of just one, I wrote a script, which basically does this:
$base_lang = require('lang/en/common.php');
$language_to_merge = require('lang/da/common.php');
$merged_lang = array_replace_recursive($base_lang, $language_to_merge);
file_put_contents('lang/da/common.php', var_export($merged_lang, true));
The problem
So far, so good. Now, lets say that $name = "John Doe";. By the very nature of PHP, after running this script the lang/da/common.php will now be
<?php return [
"yes"=>"Ja",
"no"=>"Nej",
"hello"=>"Hej John Doe!",
"newstring"=>"This string does not exist in the other language file",
"random_number"=>"Random number: 4"
];
As you might have guessed, the unwanted result is in the hello and random_number-indexes. Preferably it should still be "hello"=>"Hej {$name}!" and "random_number"=>"Random number: ".rand(1,10), but obviously that ain't happening due to PHP parsing the array values, which basically tells me that this is the wrong strategy.
Wanted result:
<?php return [
"yes"=>"Ja",
"no"=>"Nej",
"hello"=>"Hej {$name}!",
"newstring"=>"This string does not exist in the other language file",
"random_number"=>"Random number: ".rand(1,10)
];
The "how the h.. do I do that?"
Any idea how to get around this? I could file_get_contents() and do some regex, but i worry that there's too many error sources involved in that.
Thanks in advance!
EDIT
Some people have recommended using single quotes. While that actually answers my question, I became aware that I wasn't precise enough; when the Language class treats the files, I want the values to be parsed (the normal behaviour) - but only when I run my merge-script I want the actual literal variable references to remain intact.
EDIT 2 - Temporary workaround
Until I find a proper solution for this, I'm just running through the arrays of the base language, checking for key existences in the language I'm trying to fill with the missing keys - and appending a comment to the bottom of those files.

Edit: See OP's comment. This is true, but not the answer to his question :)
You should use single quotes instead of double quotes:
<?php return [
"yes"=>'Yes',
"no"=>'No',
"hello"=>'Hello {$name}!'
"newstring"=>'This string does not exist in the other language file'
];
PHP only parses variables inside double-quotes.

Related

Using arrays keys without quotes, how to fix it on big project? [duplicate]

This question already has answers here:
How to fix associative array keys that lack single quotation marks in multiple files
(4 answers)
Closed 7 months ago.
I started a project using much arrays keys without quotes.
And now I having problems with this method, i didn't knew it was bad when i started my project. I finally wanted to display E_NOTICES errors for reasons but it crash because the log is overloaded with millions notices like PHP Notice: Use of undefined constant message - assumed 'key'.
So to fix it I could add quotes to my keys in my whole project but there are so much ! Is there a way to achieve this with an algorithme or anything to fix my code ? I want replace any undefined constant by a string with quotes, EG:
$my_array[key] by $my_array['key'].
Thanks.
EDIT:
I succeeded to fix all declarations using rejex, like this:
\[([^0-9\$\'\"\]])([^\'\"\]]*)\] to \[\'\1\2\'\]
But it is not enough, there are much situations where unquoted keys are used without brackets, EG:
array_key_exists(unquotedKey,$array)
$array['key'] = array( unquotedKey => array(96,56) );
etc...
I could fix all situations using regex but I guess I will have much troubles to handle it well, and sometimes keys of my arrays are really constants and it shouldn't be quoted ! If anybody have a better solution it would help me a lot.
The perfect solution would be to be able to get my code after PHP replaced undefined constants by quoted strings, is it possible ? It does it each time I compile, it is maybe stored somewhere temporarily.
I use Notepad++ which has a search and replace in files feature (Ctrl + Shift + F). With regular expression mode on, you could use
Search:
\$my_array\[([^\'\"]+)\]
Replace
\$my_array\[\'$1\'\]
The search looks for anything within the array key square brackets where there is not already a " or ' character, which would indicate the declaration is already valid.
Select the directory of your project then hit "Replace in Files". Ensure your entire project is backed up first in case something goes wrong.
Use this pattern
[a-zA-Z0-9]{1,}(\[([^'"$0-9]+)\])
to find array keys without quotes like this:
$_POST[content_id]

Can you explain Perl's hash system to a PHP guy?

How do Perl hashes work?
Are they like arrays in PHP or some completely different beast?
From what I understand all it is is an associative array right? This is what I thought until I began
to talk to a Perl programmer who told me I was completely wrong, but couldn't explain it in a way
that didn't make my eyes cross.
Anyway, the way that I thought it worked was like this
PHP's:
$argv['dog_name'] = 'missy';
$argv[0] = 'tree';
same as Perl's:
my %argv{'dog_name'} = 'missy';
my $argv[0] = 'tree';
Right? But you cannot print(%argv{'dog_name'}), you have to (revert?) to print($argv{'dog_name'}) which is confusing?
Is it trying to print as a variable now, like you would in PHP, echo $argv['dog_name']; ? Does this mean (again) that a hash is
just a PHP associative array with a % to declare but a $ to access?
I don't know, I'm hoping some PHP/Perl Guru can explain how hashes work, and how similar they are
to PHP's arrays.
To write
$argv['dog_name'] = 'missy';
$argv[0] = 'tree';
in Perl, you would write it as follows:
$argv{dog_name} = 'missy';
$argv{0} = 'tree';
if you had strict on, which you should, then you will need to predeclare the variable:
my %argv;
$argv{dog_name} = 'missy';
$argv{0} = 'tree';
If the above is a bit repetitive for you, you could write it:
my %argv = (
dog_name => 'missy',
0 => 'tree',
);
You can find more detail on the perldata manpage.
In short, the reasons why the sigils change from % to $ is that %hash refers to a plural hash (a list of key value pairs), and $hash{foo} refers to a single element of a hash. This is the same with arrays, where # refers to the full array, and $ refers to a single element. (for both arrays and hashes a leading # sigil with a subscript means a slice of the data, where multiple keys are passed and a list of values are returned)
To elaborate slightly on Ambrose's answer, the reason for your confusion is the difference between the philosophy of using sigils in Perl and PHP.
In PHP, the sigil is attached to the identifyer. E.g. a hash identifyer will ALWAYS have a hash sigil around it.
In Perl, a sigil is attached to the way you are accessing the data structure (are you accessing 1 value, a list of values, or a whole hash of values) - for details see other excellent answers such as Eric's.
%argv{'dog_name'} is a syntax error. You need $argv{'dog_name'} instead.
But you are correct that a perl hash is just an associative array (why perl chose to use a different terminology, I don't know).
For a complete understanding of hashes, I recommend reading any of the vast number of perl tutorials or books that cover the topic. Programming Perl is an excellent choice, or here's a random online tutorial I found as well.
I would, as Flimzy, also recommend Programming Perl. As a recent PHP to Perl convert myself, it has taught me a great deal about the language.
The % symbol is used to create a full 'associative array', as we would think of it. For example, I could create an associative array by doing the following:
%hash = ('key1' => 'value1', 'key2' => 'value2');
I could then print it out like so:
print %hash;
The output would be something like:
'key2value2key1value1'
This is, I believe, known as 'list context', since the % indicates that we are talking about a range of values.
On the other hand, if I wanted to access a single value, we would have to use the $ sigil. This, as 'Programming Perl' tells us, can be thought of as an 'S' for 'Scalar'. We have to use the $ sign whenever we are talking about a singular value.
So, to access an individual item in the array, I would have to use the following syntax:
print $hash{'key1'};
The same is true of arrays. A full array can be created like so:
#array = ('abc', '123');
and then printed like so:
print #array;
But, to access a single element of the array I would type instead:
print $array[0];
There are lots of basic principles here. You should read about 'list context' and 'scalar context' in some detail. Before long you will also want to look at references, which are the things you use to create multimensional structures in Perl. I really would recommend 'Programming Perl'! It was a difficult read in chapters, but it certainly does cover everything you need to know (and more).
The sigil changing really isn't as complicated as you make it sound. You already do this in English without thinking about it.
If you have a set of cars, then you would talk about "these cars" (or "those cars"). That's like an array.
my #cars = ('Vauxhall', 'Ford', 'Rolls Royce');
If you're talking about just one car from that set, you switch to using "this car". That's like a single element from an array.
say $car[1]; # prints 'Ford';
Similar rules also apply to hashes.
I would say your confusion is partly caused by one simple fact. Perl has different sigils for different things. PHP has one sigil for everything.
So whether you're putting something into an array/hash, or getting something out, or declaring a simple scalar variable, in PHP you always use the dollar sign.
With perl you need to be more specific, that's all.
The "sigil", i.e. the character before the variable name, denotes the amount of data being accessed, as follows:
If you say $hash{key}, you are using scalar context, i.e. one value.
For plural or list context, the sigil changes to #, therefore #hash{('key1', 'key2')} returns a list of two values associated with the two keys respectivelly (might be written as #hash{qw(key1 key2)}, too).
%hash is used to acces the hash as a whole.
The same applies to arrays: $arr[0] = 1, but #arr[1 .. 10] = (10) x 10.
I hope that you are not expecting to get a full tutorial regarding perl hashes here. You don't need a Perl guru to explain you hashes, just a simple google search.
http://www.perl.com/pub/2006/11/02/all-about-hashes.html
PS: please increase your accept ratio - 62% is pretty low

Echoing variable construct instead of content

I'm merging two different versions of a translations array. The more recent version has a lot of changes, over several thousand lines of code.
To do this, I loaded and evaluated the new file (which uses the same structure and key names), then loaded and evaluated the older version, overwriting the new untranslated values in the array with the values we already have translated.
So far so good!
However, I want to be able to echo out the constructor for this new merged array so I can cut and paste it into the new translation file, and have job done (apart from completing the rest of the translations..).
The code looks like this (lots of different keys, not just index):
$lang["index"]["chart1_label1"] = "Subscribed";
$lang["index"]["chart1_label2"] = "Unsubscribed";
And the old..
$lang["index"]["chart1_label1"] = "Subscrito";
$lang["index"]["chart1_label2"] = "Não subscrito";
After loading the two files, I end up with a merged $lang array, which I then want to echo out in the same form, so it can be used by the project.
However, when I do something like this..
foreach ($lang as $key => $value) {
if (is_array($value)) {
foreach ($value as $key2 => $value2) {
echo "$lang['".$key."']"; // ... etc etc
}
}
}
..obviously I get "ArrayIndex" etc etc instead of "$lang". How to echo out $lang without it being evaluated..? Once this is working, can add in the rest of the brackets etc (I realise they are missing), but just want to make this part work first.
If there's a better way to do this, all ears too!
Thanks.
edit:
What I was really looking for was this:
"Note: Unlike the three other syntaxes, variables and escape sequences for special characters will not be expanded when they occur in single quoted strings."
So no need even to escape. May come in handy one day for someone... about merging the arrays, there's a great recursive array merging function on the PHP manual page for array_merge(), which also came in handy.
Thanks to whoever downvoted! Love you too.
Either escape the $: "\$lang" or use single quotes: '$lang'
Note that you really don't need to post a whole page of backstory, if your entire question just boils down to "how do I output a dollar sign in PHP"
there is var_export() function, but I do not understand the purpose of this terrible mess.
It's all too... manual
why not to use a gettext - an industry standard for multi-language site?
or at least some other format, more reliable than plain PHP one?
Programming code is not intended to be written automatically.

Is it okay to use array[key] in PHP?

Is it okay to use array without single or double quotion like $array[key]? I thought it is bad because PHP look for constant first if I don't use single or double quotation. One of my colleagues told me that it does not matter.
What do you guys think?
It is not considered as OK -- even if it will work in most cases.
Basically, when PHP sees this :
echo $array[key];
It will search for a constant, defined with define, called key -- and, if there is none, if will take the 'key' value.
But, if there is something like this earlier in your code :
define('key', 'glop');
It will not take
echo $array['key'];
anymore ; instead, it'll use the value of the key constant -- and your code will be the same as :
echo $array['glop'];
In the end, not putting quotes arround the key's name is bad for at least two reasons :
There is a risk that it will not do what you expect -- which is very bad
It might, today...
But what about next week / month / year ?
Maybe, one day, you'll define a constant with the wrong name ;-)
It's not good for performance :
it has to search for a constant, before using 'key'
And, as said in a comment, it generates notices (even if you disable error_reporting and display_errors, the notices/warnings/errors are still generated, even if discarded later)
So : you should not listen to that guy on this point : he is wrong : it does matter.
And if you need some "proof" that's "better" than what people can tell you on stackoverflow, you can point him to this section of the manual, as a reference : Why is $foo[bar] wrong?
This is not okay and to add to what others have said, it will trigger an error in most cases:
8 Notice Use of undefined constant key - assumed 'key' in file: 'index.php' on line 46
See the section in the PHP Manual for "Why is $foo[bar] wrong?" under "Array do's and don'ts" on this page: http://php.net/manual/en/language.types.array.php
This is wrong and will auto-define a constant:
$var = $array[bar];
This usage however is correct:
$var = "string $array[bar] ...";
For compatibility with PHP2 this old syntax is still allowed in string context. Quoting the key would lead to a parse error, unless you also use { curly braces } around it.
From the PHP Manual - Why is $foo[bar] wrong?
Always use quotes around a string literal array index. For example, $foo['bar'] is correct, while $foo[bar] is not. But why? It is common to encounter this kind of syntax in old scripts:
<?php
$foo[bar] = 'enemy';
echo $foo[bar];
// etc
?>
This is wrong, but it works. The reason is that this code has an undefined constant (bar) rather than a string ('bar' - notice the quotes). PHP may in future define constants which, unfortunately for such code, have the same name. It works because PHP automatically converts a bare string (an unquoted string which does not correspond to any known symbol) into a string which contains the bare string. For instance, if there is no defined constant named bar, then PHP will substitute in the string 'bar' and use that.
There is some more examples in the manual for you to check out.
Unless the key actually is a constant, there is no reason for you not to be putting quotes around the key.
The way PHP works is it looks for the constant value of what you've put, but it takes the string representation of it if the constant cannot be found.
If someone were to edit your code down the road and add a constant with that key name, it would just cause more headaches.
It's bad practice to not quote key values, for a number of reasons:
Potential collisions with meaningful symbol names, such as define'd constants.
Some keys can't be expressed without quoting (for instance, the key "]").
Bad habits can bite you later on (namely in regards to #1 and #2).
Performance - searching for define's takes time.
If you're wanting to avoid typing quotes around names that are just standard elements of a thing you're passing around a lot, perhaps you might want to use objects instead, which take a object->property syntax instead of an $array["element"] syntax.

Problem with PHP Array

Why is it not possible to do something equivalent to this in PHP:
(Array(0))[0];
This is just for sake of argument, but it seems strange it does not allow access of anonymous objects. I would have to do something like the following:
$array = Array(0);
$array[0];
Any ideas why this is the behavior of PHP?
I read something somewhat detailed about this once and I regret not bookmarking it because it was quite insightful. However, it's something along the lines of
"Because the array does not exist in memory until the current statement (line) executes in full (a semicolon is reached)"
So, basically, you're only defining the array - it's not actually created and readable/accessible until the next line.
I hope this somewhat accurately sums up what I only vaguely remember reading many months ago.
This language feature hasn’t been inplemented yet but will come in PHP 6.
I guess the short answer is: nobody has coded it yet. I've used (and loved) that syntax in both Python and Javascript, but still we wait for PHP.
The main reason is because unlike some languages like Python and JavaScript, Array() (or in fact array()) is not an object, but an language construct which creates an inbuilt data type.
Inbuilt datatypes themselves aren't objects either, and the array() construct doesn't return a reference to the "object" but the actual value itself when can then be assigned to a variable.

Categories