Programming Multi-Language PHP applications - php

I'm developing a PHP application and I'm wondering about the best way to include multi-language support for users in other countries.
I'm proficient with PHP but have never developed anything with support for other languages.
I was thinking of putting the language into a PHP file with constants, example:
en.php could contain:
define('HZ_DB_CONN_ERR', 'There was an error connecting to the database.');
and fr.php could contain:
define('HZ_DB_CONN_ERR', 'whatever the french is for the above...');
I could then call a function and automatically have the correct language passed in.
hz_die('HZ_DB_CONN_ERR', $this);
Is this a good way of going about it?
-- morristhebear.

Weird. People seem to be ignoring the obvious solution. Your idea of locale-specific files is fine. Have en.php:
define('LOGIN_INCORRECT','Your login details are incorrect.');
...
Then, assuming you have a global config/constants file (which I'd suggest for many reasons), have code like this:
if ($language == 'en') {
reqire_once 'en.php';
} else if ($language == 'de') {
require_once 'de.php';
}
You can define functions for number and currency display, comparison/sorting (ie German, French and English all have different collation methods), etc.
People often forget PHP is a dynamic language so things like this:
if ($language == 'en') {
function cmp($a, $b) { ... }
} else if ($language == 'de') {
function cmp($a, $b) { ... }
}
are in fact perfectly legal. Use them.

You can use gettext or something which supports gettext as well as more such as Zend_Translate.
Edit:
Just for precision, Zend_Translate supports gettext without the gettext module. You can see here the many different types of input it supports.
If you use arrays as also suggested you can also use this with Zend_Translate. The point being, if you use arrays today and gettext, xml or something else tomorrow you only have to change your config for Zend_Translate.

I really love the following approach:
one file is the translator itself:
class Translator{
private static $strs = array();
private static $currlang = 'en';
public static function loadTranslation($lang, $strs){
if (empty(self::$strs[$lang]))
self::$strs[$lang] = array();
self::$strs[$lang] = array_merge(self::$strs[$lang], $strs);
}
public static function setDefaultLang($lang){
self::$currlang = $lang;
}
public static function translate($key, $lang=""){
if ($lang == "") $lang = self::$currlang;
$str = self::$strs[$lang][$key];
if (empty($str)){
$str = "$lang.$key";
}
return $str;
}
public static function freeUnused(){
foreach(self::$strs as $lang => $data){
if ($lang != self::$currlang){
$lstr = self::$strs[$lang]['langname'];
self::$strs[$lang] = array();
self::$strs[$lang]['langname'] = $lstr;
}
}
}
public static function getLangList(){
$list = array();
foreach(self::$strs as $lang => $data){
$h['name'] = $lang;
$h['desc'] = self::$strs[$lang]['langname'];
$h['current'] = $lang == self::$currlang;
$list[] = $h;
}
return $list;
}
public static function &getAllStrings($lang){
return self::$strs[$lang];
}
}
function generateTemplateStrings($arr){
$trans = array();
foreach($arr as $totrans){
$trans[$totrans] = Translator::translate($totrans);
}
return $trans;
}
the language-files can be simply include()d and look like this:
en.php:
Translator::loadTranslation('en', array(
'textfield_1' => 'This is some Textfield',
'another_textfield ' => 'This is a longer Text showing how this is used',
));
de.php:
Translator::loadTranslation('de', array(
'textfield_1' => 'Dies ist ein Textfeld',
'another_textfield ' => 'Dies ist ein längerer Text, welcher aufzeigt, wie das hier funktioniert.',
));
in you app you can do either translate one string like this:
$string = Translator::translate('textfield_1')
or even a bunch of strings:
$strings = generateTemplateStrings(array('textfield_1', 'another_textfield'));
Because the language-files can just be included, you can stack the very easily and, say, include a global file first and then include files from submodules which can either add new strings or replace already defined ones (which doesn't work with the define()-method).
Because it's pure PHP, it can even be opcode-cached very easily.
I even have scripts around which generate CSV-files containing untranslated strings from a file and even better: convert the translated CSV-file back to the language file.
I have this solution in productive use since 2004 and I'm so very happy with this.
Of course, you can even extend it with conventions for pluralizing for example. Localizing number formats is something you'd have to do using other means though - the intl-extension comes to mind.

Thanks to all the people for your approaches and solutions. I came here with the same beggining doubt, and i can conlude, that to use the "define method" is more realistic, as, even with the odd problem that doesn't use very stylish methods with other libraries or some, being realistic and in a productive thinking, is so easy to mantain, and understand, so, you can get other languages trough non programing skills dudes.
I consider better that the arrays method, as you don't need to distribute that big multilanguage to get some traductions, besides, you are using less memory as you are loading only the needed traducted strings.
About the thing that, those can't be redefined; well, generrally if you are defining constants is because they don't need or must not be changed, so if needed that you can use a section for defined strings and variable ones.
I addmint that i don't like the way those define files looks but, take in count that your visitors are not worried at all about the way idiom is implemented, is a perfect valid implementations and you'll can easily get some traductions.

You might want to look at a framework like CakePHP or CodeIgniter that make writing internationalized applications much easier. It's not just strings you have to consider -- things like number formats and date formats also have to be accounted for

Your solution should work fine as long as it is solely intended for translating. As others have mentioned, there are many other locale-based variables, such as currency and date formats.
A solid approach to making your application locale-proof would be to use Zend_Locale combined with Zend_Translate.
Zend_Locale allows you to easily detect the user's locale, or set it if you wish. This class is useful for automatically setting the correct currency format, for example.
Zend_Translate allows you to easily translate text using several different formats:
Array
CSV
Gettext
Ini
Tbx
Tmx
Qt
Xliff
XmlTm

Your approach is workable, in the case where you include different constantfiles depending on which language the user has choosen.
You could also skip the constant-part and just define a big hashtable with constant => translation to prevent crashes in namespace.
file: en.php
$constants = Array(
'CONSTANT_KEY' => 'Translated string'
);
file: functions.php
function getConstant($key, $fallback) {
// ... return constant or fallback here.
}
However, when it comes to large amount of data, this approach will be hard to maintain. There are a few other approaches that might serve your purpose better, for instance, an ideal solution is where all your constants are stored in a global memoryspace for your entire site, to avoid having each and every reguest/thread keeping all this translated data in memory. This requires some sort php module-approach. Gettext as someone here suggested might use that approach.
Google for PHP localization to find useful resources.

One tip for you, is to create a function that returns the translated string, and if it is not in the hashtable, then return the hash key you requested with something like a * behind it to notify you it needs translation.

Here is how I do it:
parent_script.php:
$lang_pick = "EN"; //use your own method to set language choice
require_once('trans_index.php');
echo $txt['hello'];
trans_index.php :
$text = array();
$text['hello'] = array (
"EN"=> "Hello",
"FR"=> "Bonjour",
"DE"=> "Guten Tag",
"IT"=> "Ciao"
); //as many as needed
foreach($text as $key => $val) {
$txt[$key] = $text[$key][$lang_pick];
}
That may be too simple for your needs, but I find it very workable. It also makes it very easy to maintain the multiple versions of the text.

Related

PHP localization architecture with global $lang array

I'm localizing my site and using an array to store language strings. This is my architecture:
One file per language, for example: language_en.php for English, language_fr.php for French that gets included in the beginning of the script.
In each language file I have an array (consisting of about 2000 elements):
static $phrases = array();
$phrases['del'] = 'Remove';
...
Then I have a function:
function phrase($key) {
global $phrases;
return $phrases[$key]
}
Where-ever I need a localized string further in my app, I use:
phrase('del');
Couple of questions around this architecture:
$language is global, does this have negative performance
effects when the array gets larger and larger?
Would it be better to add the array right into the phrase() function and avoid it to be global?
There's nothing wrong regarding that approach.
Even though arrays grow up sometime, it shouldn't cause performance penalty. Also, it's okay using global in this case.
If you've "global paranoid" you can use closures to create a copy of global variable, like this:
$phrase = function() use ($phrases)
{
return $phrases[$key];
};
I advise you to use symfony/translation component in order to manage the translation catalogs. Symfony trabslations is a great component.
Maybe you want take a look on tis answer (mine): Best way to organize your localized translation file

PHP Function Arguments - Use an array or not?

I like creating my PHP functions using key=>value pairs (arrays) as arguments instead of individual parameters.
For example, I prefer:
function useless_func($params) {
if (!isset($params['text'])) { $params['text'] = "default text"; }
if (!isset($params['text2'])) { $params['text2'] = "default text2"; }
if (!isset($params['text3'])) { $params['text3'] = "default text3"; }
echo $params['text'].$params['text2'].$params['text3'];
return;
}
And I don't like:
function useless_func($text = "default text", $text2 = "default text2", $text3 = "default text3") {
echo $text.$text2.$text3;
return;
}
I had first seen things done this way extensively in the Wordpress codebase.
The reason I prefer arrays:
Function arguments can be provided in any order
Easier to read code / more self documenting (in my opinion)
Less prone to errors, because when calling a function I must investigate the proper array keys
I was discussing this with a co-worker and he says that it's useless and just leads to extra code and it's much harder to set the default values. Basically, he disagrees with me completely on all three points.
I am looking for some general advise and guidance from experts who might be able to provide insight: What's the better or more proper way to do this?
Don't do that!
Passing all in an array is a bad idea most of the time.
It prevents people from using your function without knowing what it needs to operate.
It lets you create functions needing lots of parameters when probably you should create a function with more precise argument needs and a narrower goal
It seems like the contrary of injecting in a function what it needs.
Function arguments can be provided in any order
I have no such preference. I don't understand that need.
Easier to read code / more self documenting (in my opinion)
Most IDEs will present you with the different arguments a function needs. If one sees a function declaration like foo(Someclass $class, array $params, $id) it is very clear what the function needs. I disagree that a single param argument is easier to read or self documenting.
Less prone to errors, because when calling a function I must investigate the proper array keys
Allowing people to pass in an array without knowing that values will be defaulted is not close to "not error-prone". Making it mandatory for people to read your function before using it is a sure way for it never to be used. Stating that it needs three arguments along with their defaults is less error prone because people calling your function will know which values the parameters will be defaulted to, and trust that it will present the result they expect.
If the problem you are trying to solve is a too great number of arguments, the right decision is to refactor your functions into smaller ones, not hide function dependencies behind an array.
Well, it's kinda usefully. But for some arguments which is passing always it's better to use classic passing like function some($a1, $a2). I'm doing like this in my code:
function getSome(SomeClass $object, array $options = array())
{
// $object is required to be an instance of SomeClass, and there's no need to get element by key, then check if it's an object and it's an instance of SomeClass
// Set defaults for all passed options
$options = array_merge(array(
'property1' => 'default1',
'property2' => 'default2',
... => ...
), $options);
}
So, as you can see I like that code style too, but for core-arguments I prefer classic style, because that way PHP controls more things which should I, if I used the you code style.
I'm assuming you're asking whether it's A Good Thing to write all functions so that they accept only one argument, and for that argument to be an array?
If you're the only person who's ever going to work on your code then you can do what you like. However, by passing all argument values through an array, anyone else is going to have to work harder to understand what the function does and why / how they could use it, especially if they're using an IDE with auto-complete for function names etc. They don't call it a "function signature" for nothing.
I'd recommend that array parameters are reserved either for items where you don't know how many there will be (e.g. a series of data items), or for groups of related options / settings (which may be what's going on in the Wordpress example that you mention?).
If you do continue with a blanket approach to array arguments then you should at least be aware of its impact on readability and take some steps to counter that issue.
Your co-worker is right. Not only is it more code for the same functionality, it is harder to read and probably has lowered performance (Since you need to call isset for each param and you need to access an array to set values).
This borders on Cargo Cult programming. You say this is more readable and self-documenting. I would ask how? To know how to use your function/method I have to read into the code itself. There's no way I can know how to use it from the signature itself. If you use any half-decent IDE or editor that supports method signature hinting this will be a real PITA. Plus you won't be able to use PHP's type-hinting syntax.
If you find you are coding a load of parameters, especially optional parameters then it suggests there might be something wrong with your design. Consider how else you might go about it. If some or all of the parameters are related then maybe they belong to their own class.
Using array_merge() works okay, but using the + operator can be used too; it works the other way, it only adds default values where one hasn't been given yet.
function useless_func(array $params = array())
{
$params += array(
'text' => 'default text',
'text2' => 'default text2',
'text3' => 'default text3',
);
}
See also: Function Passing array to defined key
A few things you don't get with using arrays as function arguments is:
type checking (only applicable to objects and arrays, but it can be useful and in some cases expected).
smart(er) text editors have a code insight feature that will show the arguments a function understands; using arrays takes away that feature, though you could add the possible keys in the function docblock.
due to #2 it actually becomes more error prone, because you might mistype the array key.
Your co-worker is crazy. It's perfectly acceptable to pass in an array as a function argument. It's prevalent in many open source applications including Symfony and Doctrine. I've always followed the 2 argument rule, if a function needs more than two arguments, OR you think it will use more than two arguments in the future, use an array. IMO this allows for the most flexibility and reduces any calling code defects which may arise if an argument is passed incorrectly.
Sure it takes a little bit more work to extrapolate the values from the array, and you do have to account for required elements, but it does make adding features much easier, and is far better than passing 13 arguments to the function every time it needs to be called.
Here is a snippet of code displaying the required vs optional params just to give you an idea:
// Class will tokenize a string based on params
public static function tokenize(array $params)
{
// Validate required elements
if (!array_key_exists('value', $params)) {
throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
}
// Localize optional elements
$value = $params['value'];
$separator = (array_key_exists('separator', $params)) ? $params['separator'] : '-';
$urlEncode = (array_key_exists('urlEncode', $params)) ? $params['urlEncode'] : false;
$allowedChars = (array_key_exists('allowedChars', $params)) ? $params['allowedChars'] : array();
$charsToRemove = (array_key_exists('charsToRemove', $params)) ? $params['charsToRemove'] : array();
....
I have used arrays to substitute a long list of parameters in many occasions and it has worked well. I agree with those in this post that have mentioned about code editors not being able to provide hints for the arguments. Problem is that if I have 10 arguments, and the first 9 are blank/null it just becomes unwieldy when calling that function.
I would also be interested in hearing an how to re-design a function that requires a lot of arguments. For example, when we have a function that builds SQL statements based on certain arguments being set:
function ($a1, $a2, ... $a10){
if($a1 == "Y"){$clause_1 = " something = ".$a1." AND ";}
...
if($a10 == "Y"){$clause_10 = " something_else = ".$a10." AND ";}
$sql = "
SELECT * FROM some_table
WHERE
".$clause_1."
....
".$clause_10."
some_column = 'N'
";
return $sql;
}
I would like to see PHP entertain adding a native helper function that could be used within a the function being called that would assist in passing an array of parameters by undertaking the necessary type checking. PHP recognized this to a certain extent by creating the func_get_args() function which allows arguments to be passed in any order. BUT this will only pass a COPY of the values, so if you want to pass objects to the function this will be a problem. If such a function existed, then the code editors would be able to pick this up and provide details on possible arguments.
#Mike, you could also "extract()" your $params argument into local variables, like this:
// Class will tokenize a string based on params
public static function tokenize(array $params)
{
extract($params);
// Validate required elements
if (!isset($value)) {
throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
}
// Localize optional elements
$value = isset($value) ? $value : '';
$separator = isset($separator) ? $separator] : '-';
$urlEncode = isset($urlEncode) ? $urlEncode : false;
$allowedChars = isset($allowedChars) ? $allowedChars : array();
$charsToRemove = isset($charsToRemove) ? $charsToRemove : array();
....
Same implementation, but shorter.

Undefine PHP defines?

I'm working on converting an old define()-based language/translation system to a more flexible one (probably JSON-based, but it's still open).
As part of this conversion, I will need to convert from 42 .php files with several thousand strings each to whatever format I'll be using. Some of the defined strings reference other defines or use PHP code. I don't need to keep this dynamic behaviour (it's never really dynamic anyway), but I will need to have the "current" values at time of conversion.
One define might look like this:
define('LNG_Some_string', 'Page $s of $s of our fine '.LNG_Product_name);
Since all defines have an easily recognizable 'LNG_' prefix, converting a single file is trivial. But I'd like to make a small script which handles all 42 in one run.
Ideally I'd be able to either undefine or redefine the define()'s, but I can't find a simple way of doing that. Is this at all possible?
Alternatively, what would be a good way of handling this conversion? The script will be one-off, so it doesn't need to be maintainable or fast. I just want it fully automated to avoid human error.
if speed is not important, so you can use get_defined_constants function.
$constans = get_defined_constants(true);
$myconst = array();
$myconst = $constans['user'];
$myconst will contain all constants defined by your script:-)
P.S: I'm not a good php coder, it was just a suggestion :-)
You can't undefine constants, but you can generate your new scripts by utiliising them and the constant() function:
<?php
/* presuming all the .php files are in the same directoy */
foreach (glob('/path/*.php') as $file) {
$contents = file_get_contents($file);
$matches = array();
if (!preg_match('/define\(\'LNG_(\w+)\'/', $contents, $matches) {
echo 'No defines found.';
exit;
}
$newContents = '';
include_once $file;
foreach ($matches as $match) {
$newContents .= "SOME OUTPUT USING $match AS NAME AND " . constant($match) . " TO GET VALUE";
}
file_put_contents('new_'.$file, $newContents);
}
?>
Defined constants can't be undefined. They're immutable.
Perhaps what you can do is get in right before they're defined and modify them in certain circumstances.

PHP language engine

I'm writing Content Management software in PHP (which should not be bigger then 3kb when minified), but what engine should I use for languages (english, dutch, german, chinese, etc...)? I was thinking of creating a function called
function _(){}
that reads strings from a file (a .ini file or similar). But does somebody has an (preferably one with as less code as possible) engine that might be smaller or faster?
I'm not sure if these engines exist already, if not, please say and I will use the _() function.
If I were you I would make my translation function like such (which I believe is very similar to gettext): make it into an sprintf()-like function and translate based on the format string, like so:
function __() {
$a = func_get_args();
$a[0] = lookup_translation($a[0]);
return call_user_func_array("sprintf", $a);
}
Now, you can use the function simply like this:
echo __("Thanks for logging in, %s!", $username);
And in a data file somewhere you have:
"Thanks for logging in, %s!"="Merci pour enlogger, %s!" (*)
The advantages of this are:
You don't have to think up identifiers for every single message: __("login_message", $username), __("logout_message", $username), etc...
You don't immediately have to write a translation for the string, which you would have to if you just used an identifier. You can defer the translation until later, once you're done coding and everything works in English.
(Similarly) You don't have to translate all strings for all languages at once, but you can do it in chunks
For maximum convenience, I would make the __ function log untranslated messages somewhere, so you don't have to go hunting for untranslated strings. Let the system tell you what needs to be translated!
(*) Disclaimer: I don't speak French ;)
You can't use _() because this is a build-in function for internationalization. You are free to roll your own function (call it __()) or use the build-in one which uses the widespread gettext system.
Drupal, for example, uses function t() for this purposes.

Hidden Features of PHP? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
I know this sounds like a point-whoring question but let me explain where I'm coming from.
Out of college I got a job at a PHP shop. I worked there for a year and a half and thought that I had learned all there was to learn about programming.
Then I got a job as a one-man internal development shop at a sizable corporation where all the work was in C#. In my commitment to the position I started reading a ton of blogs and books and quickly realized how wrong I was to think I knew everything. I learned about unit testing, dependency injection and decorator patterns, the design principle of loose coupling, the composition over inheritance debate, and so on and on and on - I am still very much absorbing it all. Needless to say my programming style has changed entirely in the last year.
Now I find myself picking up a php project doing some coding for a friend's start-up and I feel completely constrained as opposed to programming in C#. It really bothers me that all variables at a class scope have to be referred to by appending '$this->' . It annoys me that none of the IDEs that I've tried have very good intellisense and that my SimpleTest unit tests methods have to start with the word 'test'. It drives me crazy that dynamic typing keeps me from specifying implicitly which parameter type a method expects, and that you have to write a switch statement to do method overloads. I can't stand that you can't have nested namespaces and have to use the :: operator to call the base class's constructor.
Now I have no intention of starting a PHP vs C# debate, rather what I mean to say is that I'm sure there are some PHP features that I either don't know about or know about yet fail to use properly. I am set in my C# universe and having trouble seeing outside the glass bowl.
So I'm asking, what are your favorite features of PHP? What are things you can do in it that you can't or are more difficult in the .Net languages?
Documentation. The documentation gets my vote. I haven't encountered a more thorough online documentation for a programming language - everything else I have to piece together from various websites and man pages.
Arrays. Judging from the answers to this question I don't think people fully appreciate just how easy and useful Arrays in PHP are. PHP Arrays act as lists, maps, stacks and generic data structures all at the same time. Arrays are implemented in the language core and are used all over the place which results in good CPU cache locality. Perl and Python both use separate language constructs for lists and maps resulting in more copying and potentially confusing transformations.
Stream Handlers allow you to extend the "FileSystem" with logic that as far as I know is quite difficult to do in most other languages.
For example with the MS-Excel Stream handler you can create a MS Excel file in the following way:
$fp = fopen("xlsfile://tmp/test.xls", "wb");
if (!is_resource($fp)) {
die("Cannot open excel file");
}
$data= array(
array("Name" => "Bob Loblaw", "Age" => 50),
array("Name" => "Popo Jijo", "Age" => 75),
array("Name" => "Tiny Tim", "Age" => 90)
);
fwrite($fp, serialize($data));
fclose($fp);
Magic Methods are fall-through methods that get called whenever you invoke a method that doesn't exist or assign or read a property that doesn't exist, among other things.
interface AllMagicMethods {
// accessing undefined or invisible (e.g. private) properties
public function __get($fieldName);
public function __set($fieldName, $value);
public function __isset($fieldName);
public function __unset($fieldName);
// calling undefined or invisible (e.g. private) methods
public function __call($funcName, $args);
public static function __callStatic($funcName, $args); // as of PHP 5.3
// on serialize() / unserialize()
public function __sleep();
public function __wakeup();
// conversion to string (e.g. with (string) $obj, echo $obj, strlen($obj), ...)
public function __toString();
// calling the object like a function (e.g. $obj($arg, $arg2))
public function __invoke($arguments, $...);
// called on var_export()
public static function __set_state($array);
}
A C++ developer here might notice, that PHP allows overloading some operators, e.g. () or (string). Actually PHP allows overloading even more, for example the [] operator (ArrayAccess), the foreach language construct (Iterator and IteratorAggregate) and the count function (Countable).
The standard class is a neat container. I only learned about it recently.
Instead of using an array to hold serveral attributes
$person = array();
$person['name'] = 'bob';
$person['age'] = 5;
You can use a standard class
$person = new stdClass();
$person->name = 'bob';
$person->age = 5;
This is particularly helpful when accessing these variables in a string
$string = $person['name'] . ' is ' . $person['age'] . ' years old.';
// vs
$string = "$person->name is $person->age years old.";
Include files can have a return value you can assign to a variable.
// config.php
return array(
'db' => array(
'host' => 'example.org',
'user' => 'usr',
// ...
),
// ...
);
// index.php
$config = include 'config.php';
echo $config['db']['host']; // example.org
You can take advantage of the fact that the or operator has lower precedence than = to do this:
$page = (int) #$_GET['page']
or $page = 1;
If the value of the first assignment evaluates to true, the second assignment is ignored. Another example:
$record = get_record($id)
or throw new Exception("...");
__autoload() (class-) files aided by set_include_path().
In PHP5 it is now unnecessary to specify long lists of "include_once" statements when doing decent OOP.
Just define a small set of directory in which class-library files are sanely structured, and set the auto include path:
set_include_path(get_include_path() . PATH_SEPARATOR . '../libs/');`
Now the __autoload() routine:
function __autoload($classname) {
// every class is stored in a file "libs/classname.class.php"
// note: temporary alter error_reporting to prevent WARNINGS
// Do not suppress errors with a # - syntax errors will fail silently!
include_once($classname . '.class.php');
}
Now PHP will automagically include the needed files on-demand, conserving parsing time and memory.
Easiness. The greatest feature is how easy it is for new developers to sit down and write "working" scripts and understand the code.
The worst feature is how easy it is for new developers to sit down and write "working" scripts and think they understand the code.
The openness of the community surrounding PHP and the massive amounts of PHP projects available as open-source is a lot less intimidating for someone entering the development world and like you, can be a stepping stone into more mature languages.
I won't debate the technical things as many before me have but if you look at PHP as a community rather than a web language, a community that clearly embraced you when you started developing, the benefits really speak for themselves.
Variable variables and functions without a doubt!
$foo = 'bar';
$bar = 'foobar';
echo $$foo; //This outputs foobar
function bar() {
echo 'Hello world!';
}
function foobar() {
echo 'What a wonderful world!';
}
$foo(); //This outputs Hello world!
$$foo(); //This outputs What a wonderful world!
The same concept applies to object parameters ($some_object->$some_variable);
Very, very nice. Make's coding with loops and patterns very easy, and it's faster and more under control than eval (Thanx #Ross & #Joshi Spawnbrood!).t
You can use functions with a undefined number of arguments using the func_get_args().
<?php
function test() {
$args = func_get_args();
echo $args[2]; // will print 'd'
echo $args[1]; // will print 3
}
test(1,3,'d',4);
?>
I love remote files. For web development, this kind of feature is exceptionally useful.
Need to work with the contents of a web page? A simple
$fp = fopen('http://example.com');
and you've got a file handle ready to go, just like any other normal file.
Or how about reading a remote file or web page directly in to a string?
$str = file_get_contents('http://example.com/file');
The usefulness of this particular method is hard to overstate.
Want to analyze a remote image? How about doing it via FTP?
$imageInfo = getimagesize('ftp://user:password#ftp.example.com/image/name.jpg');
Almost any PHP function that works with files can work with a remote file. You can even include() or require() code files remotely this way.
strtr()
It's extremely fast, so much that you would be amazed. Internally it probably uses some crazy b-tree type structure to arrange your matches by their common prefixes. I use it with over 200 find and replace strings and it still goes through 1MB in less than 100ms. For all but trivially small strings strtr() is even significantly faster than strtolower() at doing the exact same thing, even taking character set into account. You could probably write an entire parser using successive strtr calls and it'd be faster than the usual regular expression match, figure out token type, output this or that, next regular expression kind of thing.
I was writing a text normaliser for splitting text into words, lowercasing, removing punctuation etc and strtr was my Swiss army knife, it beat the pants off regular expressions or even str_replace().
One not so well known feature of PHP is extract(), a function that unpacks an associative array into the local namespace. This probably exists for the autoglobal abormination but is very useful for templating:
function render_template($template_name, $context, $as_string=false)
{
extract($context);
if ($as_string)
ob_start();
include TEMPLATE_DIR . '/' . $template_name;
if ($as_string)
return ob_get_clean();
}
Now you can use render_template('index.html', array('foo' => 'bar')) and only $foo with the value "bar" appears in the template.
Range() isn't hidden per se, but I still see a lot of people iterating with:
for ($i=0; $i < $x; $i++) {
// code...
}
when they could be using:
foreach (range(0, 12) as $number) {
// ...
}
And you can do simple things like
foreach (range(date("Y"), date("Y")+20) as $i)
{
print "\t<option value=\"{$i}\">{$i}</option>\n";
}
PHP enabled webspace is usually less expensive than something with (asp).net.
You might call that a feature ;-)
The static keyword is useful outside of a OOP standpoint. You can quickly and easily implement 'memoization' or function caching with something as simple as:
<?php
function foo($arg1)
{
static $cache;
if( !isset($cache[md5($arg1)]) )
{
// Do the work here
$cache[md5($arg1)] = $results;
}
return $cache[md5($arg1)];
}
?>
The static keyword creates a variable that persists only within the scope of that function past the execution. This technique is great for functions that hit the database like get_all_books_by_id(...) or get_all_categories(...) that you would call more than once during a page load.
Caveat: Make sure you find out the best way to make a key for your hash, in just about every circumstance the md5(...) above is NOT a good decision (speed and output length issues), I used it for illustrative purposes. sprintf('%u', crc32(...)) or spl_object_hash(...) may be much better depending on the context.
One nice feature of PHP is the CLI. It's not so "promoted" in the documentation but if you need routine scripts / console apps, using cron + php cli is really fast to develop!
Then "and print" trick
<?php $flag and print "Blah" ?>
Will echo Blah if $flag is true. DOES NOT WORK WITH ECHO.
This is very handy in template and replace the ? : that are not really easy to read.
You can use minus character in variable names like this:
class style
{
....
function set_bg_colour($c)
{
$this->{'background-color'} = $c;
}
}
Why use it? No idea: maybe for a CSS model? Or some weird JSON you need to output. It's an odd feature :)
HEREDOC syntax is my favourite hidden feature. Always difficult to find as you can't Google for <<< but it stops you having to escape large chunks of HTML and still allows you to drop variables into the stream.
echo <<<EOM
<div id="someblock">
<img src="{$file}" />
</div>
EOM;
Probably not many know that it is possible to specify constant "variables" as default values for function parameters:
function myFunc($param1, $param2 = MY_CONST)
{
//code...
}
Strings can be used as if they were arrays:
$str = 'hell o World';
echo $str; //outputs: "hell o World"
$str[0] = 'H';
echo $str; //outputs: "Hell o World"
$str[4] = null;
echo $str; //outputs: "Hello World"
The single most useful thing about PHP code is that if I don't quite understand a function I see I can look it up by using a browser and typing:
http://php.net/function
Last month I saw the "range" function in some code. It's one of the hundreds of functions I'd managed to never use but turn out to be really useful:
http://php.net/range
That url is an alias for http://us2.php.net/manual/en/function.range.php. That simple idea, of mapping functions and keywords to urls, is awesome.
I wish other languages, frameworks, databases, operating systems has as simple a mechanism for looking up documentation.
Fast block comments
/*
die('You shall not pass!');
//*/
//*
die('You shall not pass!');
//*/
These comments allow you to toggle if a code block is commented with one character.
My list.. most of them fall more under the "hidden features" than the "favorite features" (I hope!), and not all are useful, but .. yeah.
// swap values. any number of vars works, obviously
list($a, $b) = array($b, $a);
// nested list() calls "fill" variables from multidim arrays:
$arr = array(
array('aaaa', 'bbb'),
array('cc', 'd')
);
list(list($a, $b), list($c, $d)) = $arr;
echo "$a $b $c $d"; // -> aaaa bbb cc d
// list() values to arrays
while (list($arr1[], $arr2[], $arr3[]) = mysql_fetch_row($res)) { .. }
// or get columns from a matrix
foreach($data as $row) list($col_1[], $col_2[], $col_3[]) = $row;
// abusing the ternary operator to set other variables as a side effect:
$foo = $condition ? 'Yes' . (($bar = 'right') && false) : 'No' . (($bar = 'left') && false);
// boolean False cast to string for concatenation becomes an empty string ''.
// you can also use list() but that's so boring ;-)
list($foo, $bar) = $condition ? array('Yes', 'right') : array('No', 'left');
You can nest ternary operators too, comes in handy sometimes.
// the strings' "Complex syntax" allows for *weird* stuff.
// given $i = 3, if $custom is true, set $foo to $P['size3'], else to $C['size3']:
$foo = ${$custom?'P':'C'}['size'.$i];
$foo = $custom?$P['size'.$i]:$C['size'.$i]; // does the same, but it's too long ;-)
// similarly, splitting an array $all_rows into two arrays $data0 and $data1 based
// on some field 'active' in the sub-arrays:
foreach ($all_rows as $row) ${'data'.($row['active']?1:0)}[] = $row;
// slight adaption from another answer here, I had to try out what else you could
// abuse as variable names.. turns out, way too much...
$string = 'f.> <!-? o+';
${$string} = 'asdfasf';
echo ${$string}; // -> 'asdfasf'
echo $GLOBALS['f.> <!-? o+']; // -> 'asdfasf'
// (don't do this. srsly.)
${''} = 456;
echo ${''}; // -> 456
echo $GLOBALS['']; // -> 456
// I have no idea.
Right, I'll stop for now :-)
Hmm, it's been a while..
// just discovered you can comment the hell out of php:
$q/* snarf */=/* quux */$_GET/* foo */[/* bar */'q'/* bazz */]/* yadda */;
So, just discovered you can pass any string as a method name IF you enclose it with curly brackets. You can't define any string as a method alas, but you can catch them with __call(), and process them further as needed. Hmmm....
class foo {
function __call($func, $args) {
eval ($func);
}
}
$x = new foo;
$x->{'foreach(range(1, 10) as $i) {echo $i."\n";}'}();
Found this little gem in Reddit comments:
$foo = 'abcde';
$strlen = 'strlen';
echo "$foo is {$strlen($foo)} characters long."; // "abcde is 5 characters long."
You can't call functions inside {} directly like this, but you can use variables-holding-the-function-name and call those! (*and* you can use variable variables on it, too)
Array manipulation.
Tons of tools for working with and manipulating arrays. It may not be unique to PHP, but I've never worked with a language that made it so easy.
I'm a bit like you, I've coded PHP for over 8 years. I had to take a .NET/C# course about a year ago and I really enjoyed the C# language (hated ASP.NET) but it made me a better PHP developer.
PHP as a language is pretty poor, but, I'm extremely quick with it and the LAMP stack is awesome. The end product far outweighs the sum of the parts.
That said, in answer to your question:
http://uk.php.net/SPL
I love the SPL, the collection class in C# was something that I liked as soon as I started with it. Now I can have my cake and eat it.
Andrew
I'm a little surprised no-one has mentioned it yet, but one of my favourite tricks with arrays is using the plus operator. It is a little bit like array_merge() but a little simpler. I've found it's usually what I want. In effect, it takes all the entries in the RHS and makes them appear in a copy of the LHS, overwriting as necessary (i.e. it's non-commutative). Very useful for starting with a "default" array and adding some real values all in one hit, whilst leaving default values in place for values not provided.
Code sample requested:
// Set the normal defaults.
$control_defaults = array( 'type' => 'text', 'size' => 30 );
// ... many lines later ...
$control_5 = $control_defaults + array( 'name' => 'surname', 'size' => 40 );
// This is the same as:
// $control_5 = array( 'type' => 'text', 'name' => 'surname', 'size' => 40 );
Here's one, I like how setting default values on function parameters that aren't supplied is much easier:
function MyMethod($VarICareAbout, $VarIDontCareAbout = 'yippie') { }
Quick and dirty is the default.
The language is filled with useful shortcuts, This makes PHP the perfect candidate for (small) projects that have a short time-to-market.
Not that clean PHP code is impossible, it just takes some extra effort and experience.
But I love PHP because it lets me express what I want without typing an essay.
PHP:
if (preg_match("/cat/","one cat")) {
// do something
}
JAVA:
import java.util.regex.*;
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat")
if (m.find()) {
// do something
}
And yes, that includes not typing Int.

Categories