Question about eval in PHP 5 - php

I have been doing PHP stuff for almost one year and I have never used the function eval() though I know the usage of it.
But I found many questions about it in SO.So can someone show me a simple example in which it's necessary to use eval()?And is it a good or bad practice?

eval() is necessary to implement a "compiling" template engine, like Smarty, that uses its own language and compiles it down to php on the fly. The main function of such engines is usually something like
function render_template($path) {
$code = file_get_contents($path);
$php = $this->compile_to_php($code);
eval($php);
}
Besides that, everytime you use "include" or "require", you're actually using "eval" under the hood - so, actually, eval is one of the mostly used php constructs.

Using eval() is a bad practice, and if it turns out to be necessary to achieve something, that is usually the sign of a underlying design error.
I can't think of any situation where it is necessary to use eval(). (i.e. something can't be achieved using other language constructs, or by fixing a broken design.) Interested to see whether any genuine cases come up here where eval actually is necessary or the alternative would be horribly complex.
The only instance of where it could be necessary is for executing code coming from an external source (e.g. database records.) But this is a design error in itself IMO.

Bad application design is always such an example.

Well I have used eval once. This was for a system, where the users could enter formulas using constants fished from the underlying system.
A string like:
(N * (G - 2,7)) / E
was taken and the constants replaced with values from the system eval is then used to get a value. eval seemed like the easiest way to go.
The statement was filtered to only allow operators and uppercase letters(no two next to each other) so perhaps this is not a "real" use case of eval, but it works and is pretty readable.
That said the system in questing is huge (200k+ lines) and this is the only place that eval is used.

A command line php shell is a great example. I guess you could fork the actual php code and write your shell extensions in C instead, but it seems much more sensible to do it in php. Since the person providing the code should already have full access to the system, there's no security issue at all. Once you get php compiled with readline, this sort of thing is actually really useful.
Drupal (optionally) uses eval to allow for ready extensibility. To accomplish this it takes user (generally administrator-only) input of code to be evaluated and stores it in the database. Drupal also has lots of people making sure that there are no security holes.

Using eval is quite dangerous, if see from security side. Anyway, a lot of template engines use eval, because they should parse page and get some variables or make calculations.

Eval useful for example in such case, as register widgets in cycle in wordpress while
creating custom theme:
class PluginusNetWPTF_Widget extends PluginusNetWPTF_Core {
public static $widgets = array(
'PLUGINUSNET_RECENT_POSTS_WIDGET' => array(
'description' => 'Recent posts of selected category',
'creation' => 'PluginusNet Recent Posts',
'fields' => array('title' => 'Recent Posts', 'category' => '', 'post_number' => 3, 'show_thumbnail' => 1, 'show_exerpt' => 0),
'view' => 'recent_posts',
'form' => 'recent_posts_form'
),
//'PLUGINUSNET_RECENT_POSTS_WIDGET2' => array(),
);
public static function register_widgets() {
foreach (self::$widgets as $widget_class_name => $widget_data) {
$code = '
class '.$widget_class_name.' extends WP_Widget {
//Widget Setup
function __construct() {
//Basic settings
$settings = array("classname" => __CLASS__, "description" => __(PluginusNetWPTF_Widget::$widgets[__CLASS__]["description"], PLUGINUSNET_THEME_NAME));
//Creation
$this->WP_Widget(__CLASS__, __(PluginusNetWPTF_Widget::$widgets[__CLASS__]["creation"], PLUGINUSNET_THEME_NAME), $settings);
}
//Widget view
function widget($args, $instance) {
$args["instance"] = $instance;
echo PluginusNetWPTF_Widget::draw_html("widget/" . PluginusNetWPTF_Widget::$widgets[__CLASS__]["view"], $args);
}
//Update widget
function update($new_instance, $old_instance) {
$instance = $old_instance;
if (!empty(PluginusNetWPTF_Widget::$widgets[__CLASS__]["fields"])) {
foreach (PluginusNetWPTF_Widget::$widgets[__CLASS__]["fields"] as $key => $value) {
$instance[$key] = $new_instance[$key];
}
}
return $instance;
}
//Widget form
function form($instance) {
//Defaults
$defaults = PluginusNetWPTF_Widget::$widgets[__CLASS__]["fields"];
$instance = wp_parse_args((array) $instance, $defaults);
$args = array();
$args["instance"] = $instance;
$args["widget"] = $this;
echo PluginusNetWPTF_Widget::draw_html("widget/" . PluginusNetWPTF_Widget::$widgets[__CLASS__]["form"], $args);
}
}
';
eval($code);
register_widget($widget_class_name);
}
}
}

Related

php function for language translation

For multi-language usage of CMS, they translate terms by a function similar to
function __($word) {
include 'fr.php';
if(!empty($lang[$word])) {$translated=$lang[$word];
} else {
$translated = $word;
}
return $translated;
}
Since we need to use this function several times in a php page, as all words and phrases will be echoed by __(' '); does the function need to include the language time every time, or it will be cached for the function after first load?
Since the language file contains a complete list of words and phrased used throughout the website (thousands of key/value), pho needs to load this long array into memory every time a page is visited. Is it really the best approach to add multi-language feature to a CMS?
If you can't use gettext() for some reason, you'd be better off, with something like this, to put it into an object with the included language strings as a static array, something like:
class Message {
private static $_messages = array();
public static function setMessageLibrary($sMessageLibrary) {
require_once $sMessageLibrary;
self::$_messages = $aMsgs;
}
public static function getMessage($sMessageId) {
return isset(self::$_messages[$sMessageId]) ? self::$_messages[$sMessageId] : "";
}
}
Your message library file (included with the setMessageLibrary() static function), of which you'll have one per language, will need a variable in it called $aMsgs which might look something like:
// Messages for fr-FR
$aMsgs = array(
'hello_everybody' => "Bonjour tout le monde"
...
and so on
);
Since it's all static but within the object you can effectively cache that included language file by setting it at the start of your script.
<?php
Message::setMessageLibrary('/lang/fr-FR/messages.inc.php');
echo Message::getMessage('hello_world');
echo Message::getMessage('another_message');
echo Message::getMessage('yet_another_message');
?>
All three messages will then reference the single language array stored in Message::$_messages
There's no sanitisation, nor sanity checks in there, but that's the basic principle anyway ... if you can't use gettext() ;)
1) it won't be cached, use include_once instead
2) no, i think gettext is doing it another/better way
IIRC, it will do some caching.
No, it's not. Check out gettext.

Optimize this PHP Code?

I am currently working on a mashup that incorporates many data feeds. In order to display ALL of the feeds that the user wants on one page, I am currently using if statements to cross-check with the MySQL database like this:
if($var["type"]=="weather")
$var being the result of a call to mysqli_fetch_array
and then including code relevant to the function (e.g. weather) underneath, and then another "if" statement for another feed, so on so on. The problem is that there will be many feeds, and having all these "if" statements will be slow and redundant.
Is there any way to optimize this PHP code?
Another solution might be to map the "type" to a custom function using associative arrays.
e.g. (pseudo code)
function handle_wheater_logic() {
// ... your code goes here
}
function handle_news_logic() {
// .. your code goes here
}
$customFunctions = array("wheater" => "handle_wheater_logic", "news" => "handle_news_logic");
while ($row = mysql_fetch_...) {
call_user_func ($customFunctions[$row["type"]])
}
This would eliminate the need to use a lot of if statements. You might as well do the "type to function" mapping in a configuration file or maybe just store the name of the custom function to call for each "type" in a database table - that's up to you.
You can, of course also pass parameters to custom function. Just checkout the documentation for call_user_func[_array].
Try this:
$methods = array(
"weather" => function() {
// code
},
"otheroption" => function() {
}
);
Just use then $var["type"] as a index in the array to get the function:
$methods[$var["type"]]();
You can obviuosly, for better readbility do something similar:
$methods = array(
"weather" => "wheater_function",
"otheroption" => "other_function"
);
and then call the functions this way:
call_user_func($methods[$var["type"]]);
To be even more object oriented we can obviously store in the array objects implementing a particular interface, or store object redifining the __call() magic method and use it like functions.
You can use a switch statement.
A good solution for eliminating a lot of if statements and a huge switch statement just checking for one condition, would be to implement a design pattern such as the Strategy pattern.
This way you will have the code for each type separated, which makes it easier to overview and manage.
Here's an example of an implementation http://blogs.microsoft.co.il/blogs/gilf/archive/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements.aspx
Even if you won't implement this strictly it will give you some ideas on how to solve this elegantly.
Create an array that associates a function to each feed type:
$actions = array("weather" => "getWeather",
"news" => "getNews");
Then use call_user_func to call the correct one:
call_user_func($actions[$var["type"]]);
Polymorphysm for the rescue.
inteface FeedInterface {
public function retrieve($params);
}
class FeedWeather implements FeedInterface {
public function retrieve($params) {
//retrieve logic for weather feed
}
}
class FeedSports implements FeedInterface {
public function retrieve($params) {
//retrieve logic for sports feed
}
}
With use of PHP class autoloading, each of above declarations can be in a separate file, possibly namespaced as well. Then your feed retrieval code could look like this:
$class = 'Feed'.$var["type"];
$feed = new $class;
$feed->retrieve($params);
That's overly simplified and would need some additional code for error handling, discovery of non-existing classes and such, but the idea should be clear.
Using either an If Statement or a Switch statement will be faster than you care about. It might look ugly and be cumbersome to maintain but it will be fast.

PHP - constants/global variables/configuration

I have a script (a small and simple CMS-like system), which I'm always working on and use it for client sites. Since clients have different requirements, I've implemented a module system which allows me to modify/or add functionality to the CMS, without having to modify the CMS script.
How can I implement a configuration system that allows me to change the default settings of the main CMS from the modules?
For example the CMS has by default two menus: $menu = array('menu-1', 'menu-2');
How could I override this setting from the modules?
One solution I've thought of is to use constants and serialize/unserialize:
defined("BLA") or define("BLA", serialize(array(
'boo' => 'stuff',
'foo' => array('1', '2', '3'),
'moo' => true,
...
)));
So I could easily override this setting in the module initialization function which runs before the constant is defined in the CMS.
Then I'm using these constants everywhere inside my scripts, like:
$bla = unserialize(BLA);
...
foreach(unserialize(BLA) as $key => $value)...
Another alternative would be to use a global variable, but people say it's bad to use global.
So are there any better solutions to what I'm looking for?
I would recommend using a class with static variables. More or less the same result, but no need for unserializations and you can actually use the variable, not a temporary one.
// My Constants
class MCo {
public static $BLA = array(
'boo' => 'stuff',
'foo' => array('1', '2', '3'),
'moo' => true,
// ...
);
}
echo MCo::$BLA['boo'];
foreach (MCo::$BLA as $key => $value) {
// ...
}
EDIT: ircmaxell has a point, consider this then
// My Private Constants
class MPCo {
private static $_BLA = array(
'boo' => 'stuff',
'foo' => array('1', '2', '3'),
'moo' => true,
// ...
);
public static BLA() {
return self::$_BLA;
}
}
foreach (MPCo::BLA() as $key => $value) {
// ...
}
If it's going to be a lot of static data that you plan on storing in the array, why not store it in a config file? Alternatively, you could load it from a DB, but config is better for this.
And the other option is to do what #inti suggested in his answer.
The first question I'd ask is why do you need an array in a constant. There is likely another solution to your problem that's just as good, but doesn't need arrays.
One alternative that would be better is to create a configuration object, and pass that around. That way it's still testable since it's injecting the dependencies and doesn't have a performance impact of working with serialize.
$config = new StdClass();
$config->boo = 'stuff';
doSomething($config);
Constants aren't much better than global variables. In fact, they are worse in some respects since it's very hard to test with them since once you define a constant, you can't change it. So in the pursuit of good testable code, constants are not good. If you're dependent on constants other than for dealing with magic numbers or filesystem paths, you might want to rethink your approach (Even magic number constants might be better handled with a configuration class)...
This is really unnecessary, just use a variable instead of a constant, it will be simpler, clearer and I would guess better performing.

PHP simple translation approach - your opinion

I am doing a PHP web site, without using any framework. I need that the site is available in several languages, and I was reading about it and it seems to be a little bit confusing. There are several solutions but all seem to depend on a specific framework.
What you think of using a simple translation function like the one shown below?
I mean, I would like to know what can be a disadvantage of using such code.
Here it is (this is just a simple and incomplete sample):
class Translator{
private $translations;
public function __construct(){
$this->translations = array(
'Inbox' => array(
'en' => 'Inbox',
'fr' => 'the french word for this'
),
'Messages' => array(
'en' => 'Messages',
'fr' => 'the french word for this'
)
//And so on...
);
}
public function translate($word,$lang){
echo $this->translations[$word][$lang];
}
}
It does not look bad. I've seen this used many times.
I would however separate the different strings in one file per language. At least, or if the files get large, one file per module per language.
Then your translation class can load and cache the language files (if you don't rely on any other caching system) every time a new language is to be used.
A little example of what i mean
class Translator {
private $lang = array();
private function findString($str,$lang) {
if (array_key_exists($str, $this->lang[$lang])) {
return $this->lang[$lang][$str];
}
return $str;
}
private function splitStrings($str) {
return explode('=',trim($str));
}
public function __($str,$lang) {
if (!array_key_exists($lang, $this->lang)) {
if (file_exists($lang.'.txt')) {
$strings = array_map(array($this,'splitStrings'),file($lang.'.txt'));
foreach ($strings as $k => $v) {
$this->lang[$lang][$v[0]] = $v[1];
}
return $this->findString($str, $lang);
}
else {
return $str;
}
}
else {
return $this->findString($str, $lang);
}
}
}
This will look for .txt files named after the language having entries such as this
Foo=FOO
Bar=BAR
It always falls back to the original string in case it does not find any translation.
It's a very simple example. But there is nothing wrong in my opinion with doing this by yourself if you have no need for a bigger framework.
To use it in a much simpler way you can always do this and create a file called 'EN_Example.txt'
class Example extends Translator {
private $lang = 'EN';
private $package = 'Example';
public function __($str) {
return parent::__($str, $this->lang . '_' . $this->package);
}
}
Sometimes you wish to translate strings that contain variables. One such approach is this which i find simple enough to use from time to time.
// Translate string "Fox=FOX %s %s"
$e = new Example();
// Translated string with substituted arguments
$s = printf($e->__('Fox'),'arg 1','arg 2');
To further integrate variable substitution the printf functionality can be put inside the __() function like this
public function __() {
if (func_num_args() < 1) {
return false;
}
$args = func_get_args();
$str = array_shift($args);
if (count($args)) {
return vsprintf(parent::__($str, $this->lang . '_' . $this->package),$args);
}
else {
return parent::__($str, $this->lang . '_' . $this->package);
}
}
There are a few things it appears you haven't considered:
Are you simply translating single words? What about sentence structure and syntax that differs between languages?
What do you do when a word or sentence hasn't been translated into a language yet?
Does your translations support variables? The order of words in a sentence can differ in different languages, and if you have a variable it usually won't be good enough simply to split the word around the sentence.
There are a two solutions that I've used and would recommend for PHP:
gettext - well supported in multiple languages
intsmarty - based on Smarty templates
The advantage with using a class or functions for this is that you can change the storage of the languages as the project grows. If you only have a few strings, there is absolutely no problems with your solution.
If you have a lot of strings it could take time, memory and harddrive resources to load the language arrays on all page loads. Then you probably want to split it up to different files, or maybe even use a database backend. If using i database, consider using caching (for example memcached) so you don't need to query the database hundreds of times with every page load.
You can also check out gettext which uses precompiled language files which are really fast.
I'd have thought it might be easier to simply use an include for each language, the contents of which could simply be a list of defines.
By doing this, you'd avoid both the overhead of including all the language data and the overhead of calling your 'translate' function on a regular basis.
Then again, this approach will limit things in terms of future flexability. (This may not be a factor though.)
It's fine to not use a framework. The only problem I see with your function is that it's loading a lot of data into memory. I would recommend having arrays for each language, that way you would only need to load the language that is being used.
Is using constants (defines) a bad practice?
That's how I have it setup. It was just to have multi langua support.
I have one portuguese file and an english files filled with:
define('CONST','Meaning');
Maybe this is a bit a memory hog, but I can access from every where I want :)
I may change to a oop approach, but for now I have this.
When I had a problem like this (but for a very small site, just a few pages) a long time ago, I created a file named langpack.php and any string of text on my site had to be run through that. Now, I would use a similar approach, but split over multiple files.
Example OOP Approach
langpack.php
abstract class langpack {
public static $language = array();
public static function get($n) {
return isset(self::$language[$n]) ? self::$language[$n] : null;
}
}
english.php
final class English extends langpack {
public static $language = array(
'inbox' => 'Inbox',
'messages' => 'Messages',
'downloadError' => 'There was an error downloading your files',
);
}
french.php
final class French extends langpack {
public static $language = array(
'inbox' => 'Inbioux',
'messages' => 'Omelette du Fromage',
'downloadError' => 'C\'est la vie',
);
}
You should get the idea from there. Implement an autoloader in a config file and then loading the language should be something you could easily do from the session, URL, or whatever, by using PHP's variable nature in conjunction with class instantiation, something like this:
$langpack = new $_SESSION['language'];
echo $langpack::get('inbox');
Of course, all this could be done with simple arrays, and accessed in an imperative style (with absolute references handled via $GLOBALS) to reduce some overhead and perhaps even make the mechanisms by which this is all handled a bit more transparent, but hey, that wouldn't be very OO, would it?
One could also be using the Symfony translation component, no framework is required and composer helps dealing with dependencies:
composer install --prefer-dist "symfony/translation":"#stable"
I think that's ok if you're not using any framework for other reasons. We've been in the same scenario as yours, when you cannot/don't want to use a more structured translation framework:
We were working at a small PHP project and where looking for some simple translation mechanism. We used an array approach similar to yours, but with separate files for each language texts. We put up a small component to keep thins as clean as possible.
If you want to give a look, we shared that on https://github.com/BrainCrumbz/simple-php-translate. Please feel free to improve it!
i would simply use a function with controller and language inputs, for instance:
function getLanguageFile($controller, $lang){
// security: only allow letters a-z in both strings
$controller = preg_replace('/([^a-z]*)/', '', $controller);
$lang = preg_replace('/([^a-z]*)/', '', $lang);
// return language details if present on disk
if (is_file('lang/'.$controller.'/'.$lang.'.json')){
return json_decode(file_get_contents('lang/'.$controller.'/'.$lang.'.json'));
}
return false;
}
you simply have to place your json formatted strings in lang/index/en.json if controller is index and language is en.
you could add a function for dependencies (for instance you want to load index controller values on access to another controller) all you have to do is to merge the results.
you could simply include php files with arrays aswell and just return the array then, but i suggest you split these translations in larger projects. if your project isn't that big, your function is absolutely ok.

Most efficient way to do language file in PHP?

Questions Updated instead of making a new question...
I really want to provide a few alternative languages other then English on my social network site I am building, this will be my first time doing any kind of language translation so please bear with me.
I am researching so I am al ear and open to ideas and I have a lot already here is are the questions.
1)
What does i18n mean, I see it often when researching language translation on SO?
2)
Most people say use gettext PHP has an extension or support for it,
well I have been researching it and I have a basic understanding of it, as far as I can tell it is a lot of extra work to go this route,
I mean coding my site to use it's functions ie; _('hello world i'm in English for now') or else gettext('hello world i'm in English for now') is no problem as any route I go will require that.
But then you have to install gettext on your server and get it working,
then use some special editors to create special files and compile them I think?
Sounds like a pain, I understand this is supposed to be the best route to go though, well everyone seems to say it is.
So can someone tell me why this is the route to go?
3)
I really like the simplicity of this approach, just building a language array and calling the phrase you need in a function like the example below
, you would then just include a file with the appropriate language array.
What I really want to know is, would this be the less better performance method on a high traffic and fairly large site compared to using gettext and if so can you explain why please?
<?PHP
//Have seperate language files for each language I add, this would be english file
function lang($phrase){
static $lang = array(
'NO_PHOTO' => 'No photo\'s available',
'NEW_MEMBER' => 'This user is new'
);
return $lang[$phrase];
}
//Then in application where there is text from the site and not from users I would do something like this
echo lang('NO_PHOTO'); // No photo's available would show here
?>
* some code used from brianreavis's answer below
It'd probably be best to define a function that handles your language mapping. That way, if you do want to change how it works later, you're not forced to scour hundreds of scripts for cases where you used $lang[...] and replace them with something else.
Something like this would work and would be nice & fast:
function lang($phrase){
static $lang = array(
'NO_PHOTO' => 'No photo\'s available',
'NEW_MEMBER' => 'This user is new'
);
return $lang[$phrase];
}
Make sure the array is declared static inside the function so it doesn't get reallocated each time the function is called. This is especially important when $lang is really large.
To use it:
echo lang('NO_PHOTO');
For handling multiple languages, just have this function defined in multiple files (like en.php, fr.php, etc) and require() the appropriate one for the user.
This might work better:
function _L($phrase){
static $_L = array(
'NO_PHOTO' => 'No photo\'s available',
'NEW_MEMBER' => 'This user is new'
);
return (!array_key_exists($phrase,$_L)) ? $phrase : $_L[$phrase];
}
Thats what i use for now. If the language is not found, it will return the phrase, instead of an error.
You should note that an array can contain no more than ~65500 items. Should be enough but well, just saying.
Here's some code that i use to check for the user's language:
<?php
function setSessionLanguageToDefault() {
$ip=$_SERVER['REMOTE_ADDR'];
$url='http://api.hostip.info/get_html.php?ip='.$ip;
$data=file_get_contents($url);
$s=explode (':',$data);
$s2=explode('(',$s[1]);
$country=str_replace(')','',substr($s2[1], 0, 3));
if ($country=='us') {
$country='en';
}
$country=strtolower(ereg_replace("[^A-Za-z0-9]", "", $country ));
$_SESSION["_LANGUAGE"]=$country;
}
if (!isset($_SESSION["_LANGUAGE"])) {
setSessionLanguageToDefault();
}
if (file_exists(APP_DIR.'/language/'.$_SESSION["_LANGUAGE"].'.php')) {
include(APP_DIR.'/language/'.$_SESSION["_LANGUAGE"].'.php');
} else {
include(APP_DIR.'/language/'.DEFAULT_LANG.'.php');
}
?>
Its not done yet, but well i think this might help a lot.
Don't write your own language framework. Use gettext. PHP has standard bindings that you can install.
Don't reinvent the wheel. Use for example gettext or Zend_Translate.
As the other answers don't really answer all the questions, I will go for that in my answer plus offering a sensible alternative.
1)
I18n is short for Internationalization and has some similarities to I-eighteen-n.
2)
In my honest opinion gettext is a waste of time.
3)
Your approach looks good. What you should look for are language variables. The WoltLab Community Framework 2.0 implements a two-way language system. For once there are language variables that are saved in database and inside a template one only uses the name of the variable which will then be replaced with the content of the variable in the current language (if available). The second part of the system provides a way to save user generated content in multiple languages (input in multiple languages required).
Basically you have the interface text that is defined by the developer and the content that is defined by the user. The multilingual text of the content is saved in language variables and the name of the language variable is then used as value for the text field in the specific content table (as single-language contents are also possible).
The structure of the WCF is sadly in a way that reusing code outside of the framework is very difficult but you can use it as inspiration. The scope of the system depends solely on what you want to achieve with your site. If it is going to be big than you should definitely take a look at the WCF system. If it's small a few dedicated language files (de.php, en.php, etc), from which the correct one for the current language is included, will do.
why not you just make it as multi-dimesional array...such as this
<?php
$lang = array(
'EN'=> array(
'NO_PHOTO'=>'No photo\'s avaiable',
'NEW_MEMBER'=>'This user is new',
),
'MY'=> array(
'NO_PHOTO'=>'Tiada gambar',
'NEW_MEMBER'=>'Ini adalah pengguna baru',
)
);
?>
You can do this:
class T {
const language = "English";
const home = "Home";
const blog = "Blog";
const forum = "Forum";
const contact = "Support";
}
You would have a file like this for each language. To use the text:
There is no place like <?=T::home?>.
The downside is that if you add a new constant, you have to do it for every langauge file. If you forget one, your page breaks for that language. That is a bit nasty, but it is efficient since it doesn't need to create a large associative array and possibly the values even get inlined.
Maybe access could be improved, eg:
class T {
const home = "home";
public static function _ ($name) {
$value = #constant("self::$name");
return $value ? $value : $name;
}
// Or maybe through an instance:
public function __get ($name) {
$value = #constant("self::$name");
return $value ? $value : $name;
}
}
echo "There is no " . T::_("place") . " like " . T::_("home");
$T = new T();
echo "There is no " . $T->place . " like " . $T->home;
We still avoid the array and rely on constant to do the lookup, which I assume is more expensive than using the constants directly. The up side is the lookup can use a fallback when the key is not found.
An extension to the answers above whom deserve the credit - I'm just posting as maybe this will also be useful to someone else who ends up here.
I personally prefer the key to the array to be the actual phrase in my mother tongue (in my case English) rather than a CONSTANT_VALUE because:
I find it easier to read the code when the text is in my native language rather than having to remember what a CONSTANT_VALUE actually outputs
It means no lookup is needed to return the phrase for visitors who also use my naitive language (giving marginally better performance)
It's one less list of values to maintain
The downside is that it's harder to spot missing values in other languages as you don't necessarily have a master list anymore - I also log a warning from the abstract method so that I spot any missing values.
I implemented as:
An abstract class with static methods for outputting the text value (using late static binding)
A concrete class for each language: English overriding the method to return the phrase without translation, other languages overriding the list of phrases so that a translated phrase is returned
<?php
namespace Language;
abstract class _Language
{
protected static $displayText = array();
public static function output($phrase){
return static::$displayText[$phrase] ?? $phrase;
}
}
<?php
namespace Language;
class English extends _Language
{
public static function output($phrase){
return $phrase;
}
}
<?php
namespace Language;
class Spanish extends _Language
{
protected static $displayText = array(
'Forename' => 'Nombre',
'Registered Email' => 'Correo electrónico registrado',
'Surname' => 'Apellido'
);
}
Usage:
$language = new \Language\Spanish();
echo $language::output('Forename'); // Outputs: Nombre
$language = new \Language\English();
echo $language::output('Registered Email'); // Outputs: Registered Email
Unfortunately gettext not work good and have problems in various situation like on different OS (Windows or Linux) and make it work is very difficult.
In addition it require you set lot's of environment variables and domains and this not have any sense.
If a developer want simply get the translation of a text he should only set the .mo file path and get the translation with one function like translate("hello","en_EN"); With gettext this is not possible.

Categories