I have a code base that can be customized for different customers. This is handled, in most instances, by loading a class with constants specific to each customer, but with the same class name. Class name collision is managed, and, obviously, only one is loaded at a time.
class Customer () {
const CUST_NAME = 'Alpha Corp.';
}
class Customer () {
const CUST_NAME = 'Beta, Inc.';
}
For each customer, I also want the code to be internationalized. So, a program can draw its displayed text from a language-specific source, but also still be dynamic with regard to customer-specific information. Assume the customer-specific information is indifferent toward i18n. This reduces the number of files from one per customer per language, to just one per language. The desired result is to implement something similar to
// in separate language files:
$greeting = 'Hello. Welcome to ';
$greeting = 'Hola. Bienvenido a ';
// in front-end
$greeting = get $greeting in desired language;
echo $greeting, Customer::CUST_NAME, "\n";
The best solution I can devise to meet these requirements is an i18n class (or family of classes), which would handle multiple customers and/or multiple languages. However, it is execution-expensive to call methods, as opposed to string literals or constants. The methods would combine translations from constants or external sources, and combine them with values from the Customer class. (I am stuck with PHP 5.2 for now, so the niceties of heredoc for class properties/constants is unavailable.)
class i18n_en () {
const GREETING = 'Hello. Welcome to ';
static public function getGreeting () {
return self::GREETING . Customer::CUST_NAME;
}
}
Alternatively, I can write a script for a "template approach", in which I maintain a template file of the class, with text placeholders. The customer-specific files are generated at the time of customer creation from these templates. It would be easy to generate or re-generate files as needed, but I'm back to needing a separate file for each customer for each language. Thus, it doesn't meet my need as nicely.
Surely, I'm not the first to face this problem. Can anyone offer alternatives or best practices? Since the code will execute many times more than I'll create a new customer, I prefer run-time efficiency to ease of maintenance. Of course, a great solution will offer both.
There are two tried and true methods for PHP internationalization.
The most prevalent is to create huge language arrays like so:
define('MSG__GREETING', 1);
$lang['en_US'] = array(MSG__GREETING => 'Hello, and welcome to ');
$lang['fr_FR'] = array(MSG__GREETING => 'Bonjour, et bienvenue à ');
$selectedLang = 'fr_FR';
echo $lang[$selectedLang][MSG__GREETING] . 'Fhloston Paradise';
Unfortunately, this gets very tedious very quickly.
The ideal method, which I've used numerous times, is the accepted method of internationalization for Linux apps: il8n, via PHP's gettext extension.
With this method, you basically end up doing stuff like this:
setenv('LANG=fr_FR');
echo _('Hello, and welcome to ') . 'Fhloston Paradise';
and then in il8n files (called .po) you have each translation. It's actually much easier to maintain and extend over the long run, especially since you can just email your .po files to various translators and they just fill in the blanks, as it were... no coding skills necessary.
Here's a tutorial to get you started: http://kunststube.net/frontback/
Personally, when handling internationalization I have always used templates, specifically Smarty - http://www.smarty.net/ and create language based template. You can also use translation keys in a database, ex table with key, language, translation columns which can be cached this way you aren't calling tons of methods. Also, you can cache the output of all the translation methods in memory using memcached. Tons of ways. Hope some of these help a little.
Related
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
I am not sure if I get the overall idea of Silex right where to store "business logic" which is not directly related to persistance, views, etc.
Example: In my "demo app" I needed a way to generate n unique numbers. I needed those numbers in a template to include partial templates. I created a RandomNumberServiceProvider which can return n numbers between x and y. In my $app-closure I used this service, assigned the random numbers to my twig template. Done.
More complex example: Let's say you have to develop a "complex" import function. You have to read data from the disk, validate the data, transform it somehow and finally store it into the database. Would you also create a ImportServiceProvider in this case which accesses other services (for persistance...)?
Something like that, yes.
Though, instead of injection the complete service, I would recommend to inject just the factory. This way you can prevent the application logic from leaking in the controllers (or their equivalents), while at the same time keeping the domain objects as focused on the specific tasks.
$factory = new \My\ServiceFactory( /* ..dependencies */ );
// --- SNIP
$app->get('/foo/{bar}', function ($app, $bar) use ($factory) {
$someService = $factory->build( ... );
// do something with $someService
return new Response(...);
});
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Are global variables in PHP considered bad practice? If so, why?
global in functions
Edit: question answered in link above.
No, "global" in php is not the same thing as global in other languages, and while it does not introduce any security problems it can make the code less comprehensible to others.
OP:
Project summary - I am writing a web CMS to get my feet wet with PHP / MySQL. In order to break up the code I have a concept of these basic tiers / modules:
Data
- MySQL Tables
- PHP Variables
Function
- SQL - Get / Set / etc
- Frontend - Showing pages
- Backend - Manager
Presentation
- HTML Templates
- Page Content
- CSS Stylesheets
The goal is common enough. Use MySQL to hold site settings and page content, use php to get / manipulate content data for a page being served, then insert into an html template and echo to browser. Coming from OO languages like C# the first problem I ran into was variable scope issues when using includes and functions.
From the beginning I had been writing function-only php files and including them where needed in other files that had existing variable array definitions. For example, ignoring the data tier for a moment, a simple page might generically look like this:
File 1 (page)
$DATA_PAGE = Array
(
'temp' = null,
'title' = null,
[...]
);
include 'functions.php';
get_data ( 'page.php' );
[...]
run_html ();
File 2 (functions)
function get_data ( $page_name )
{
global $DATA_PAGE;
$DATA_PAGE [ 'temp' ] = 'template';
$DATA_PAGE [ 'title' ] = 'test page';
[...]
}
function run_html ()
{
global $DATA_PAGE;
echo '<html>';
echo '<head>';
echo '<title>' . $DATA_PAGE [ 'title' ] . '</title>';
[...]
}
I chose this method for a few reasons:
The data in these arrays after sql fetch might be used anywhere, including page content
I didnt want to have a dozen function arguments, or pass entire arrays
The code runs great. But in every article I find on the subject, the "global" calls in my functions are called bad practice, even though the articles never state why? I thought all that meant was "use parent scope". Am in introducing a security hole into my app? Is there a better method? Thanks in advance.
I think a top reason for avoiding this is that it hides dependencies.
Your functions get_data and run_html do not advertise in any way that they share data, and yet they do, in a big way. And there is no way (short of reading the code) to know that run_html will be useless if get_data has not been called.
As the complexity of your codebase grows, this kind of lurking dependency will make your code fragile and hard to reason about.
Because global variables can be modified by the process without other parts of your code knowing it producing unexpected results.
You should always try to keep variables scoped as locally as possible -- not only will this help you with debugging, but it will keep your code easier and cleaner to read and go back and modify.
If you are looking to share data across multiple functions, you might look into making a class for your data and then defining methods to operate on the data encapsulated in your object. (Object-Oriented programming)
For many reasons, for example:
Hard to support code with global variables. You don't know where global variables can affect your logic and don't control access to them
Security - if you have complex system (especially with plugins), someone can compromise all system with global variables.
Settings in a global variable are fine, but putting data that can be modified into a global variable can, as tkone said, have unexpected results.
That said, I don't agree with the notion that global variables should be avoided at all costs - just try wrapping them into, say, a singleton settings class.
It is bad practice to use variables from a different scope to your own, because it makes your code less portable. In other words, if I want to use the get_data() function somewhere else, in another project, I have to define the $DATA_PAGE variable before I can use it.
AFAIK this is the only reason this should be avoided - but it's a pretty good reason.
I would pass by reference instead.
function get_data (&$data_page, $page_name )
{
$data_page['temp'] = 'template';
$data_page['title'] = 'test page';
[...]
}
get_data($DATA_PAGE);
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.
I'm using CodeIgniter, and will likely use their template library as I want to keep things extremely simple to use. The content for the template variables will come from the database, but I want the business admins to know what content areas are available. Basically the names of the parameters when they choose a specific template. For instance, Joomla uses an extra XML file that defines each area, whereas Wordpress uses comments within a page template to inform the system that the PHP file is a template. I like the Joomla approach because you don't have to parse the PHP file to find the areas, but I like the Wordpress approach because you don't have an extra XML file associated with every template. Are there other approaches that I'm missing?
I think the nicest way would be to add a small hack to the template parser class. The code looks quite readable and clean in system/libraries/Parser.php. You could insert a hook in that class that can be used to keep track of the variables. I don't know, if it works, but here's a snippet:
class CI_Parser {
var $varCallback;
function setVarCallback($callbackFunction) {
$this->varCallback = $callbackFunction;
}
...
function _parse_single(...) {
$callback = $this->varCallback;
$callback($key);
}
...
//Somewhere in your code
function storeVarName($variableName) {
// Persist the variable name wherever you want here
}
$this->parser->setVarCallback('storeVarName');
You could do this directly in the controller:
// in the controller
print_r($data);
$this->load->view("main", $data);
Or a little more rudimentary, but you could pass to the template a PHP array of variables (or an object):
// in the controller
$data = array();
$data["namespace"] = array(
"title" => "My website",
"posts" => array("hi", "something else")
);
$this->load->view("main", $data);
And then in the view, have a flag to print_r the namespace to show all the vars available, so that business admins know exactly what to use.
// in the view
if(isset($namespace["showAllVars"])) print_r($namespace);
One option would be to call token_get_all on the PHP file (only when your business admins are loading it up), and parse the output of that.
The best approach, in my opinion, is to keep the variable definitions in another place (such as a database table, or a separate file). This will help with testing (i.e., a programmer can't just remove a tag and it's gone) and making sure things are still working as you move on with the application development in time.
Another advantage is that your application logic will be independent from the templating engine.
On a side note, if you expect a lot of traffic, you may consider using smarty instead. We have done extensive testing with most of the templating engines around and smarty is the fastest.