I've language files like "en.lang.php":
/* english language */
return array(
"random.key.one" => "random value one",
"random.key.two" => "random value two);
Usage (the quick & dirty way):
/* random_template.php */
$language = "en";
$file = $language . ".lang.php";
$text = include($file);
echo $text["random.key.one"]; // "random value one"
Question:
How is it possible to use this values in JavaScript?
Idea:
Generate en.lang.js with a function that returns the needed value, usage:
alert(get_text("random.key.one"));
Problem: I've to flush the cache everytime the *.lang.php-file was changed. Not that user-friendly.
Thanks in advance!
If you're using PHP >= 5.2.1 (.0, technically), the easiest way I can imagine is as follows:
Have a file that generates a JSON-array with
echo 'set_text(' . json_encode(include($file)) . ')';
JavaScript-wise:
var texts = [];
function set_text(languageTexts){
texts = languageTexts;
}
function get_text(key) {
return texts[key];
}
Try to cache control the page and set it to no cache so it will load from origin.
And if you want to play with PHP and JS you have to keep in mind that PHP is server side and JS is client side. So best thing i can suggest is to use Ajax. Its prety easy to use JS on the page and get JS to query the server that process data with php.
Related
I am trying to loop through all the php files listed in an array called $articleContents and extract the variables $articleTitle and $heroImage from each.
So far I have the following code:
$articleContents = array("article1.php", "article2.php"); // array of all file names
$articleInfo = [];
$size = count($articleContents);
for ($x = 0; $x <= $size; $x++) {
ob_start();
if (require_once('../articles/'.$articleContents[$x])) {
ob_end_clean();
$entry = array($articleContents[$x],$articleTitle,$heroImage);
array_push($articlesInfo, $entry);
}
The problem is, the php files visited in the loop have html, and I can't keep it from executing. I would like to get variables from each of these files without executing the html inside each one.
Also, the variables $articleTitle and $heroImage also exist at the top of the php file I'm working in, so I need to make sure the script knows I'm calling the variables in the external file and not the current one.
If this is not possible, can you please recommend an alternative method?
Thanks!
Don't do this.
Your PHP scripts should be for your application, not for your data. For your data, if you want to keep it file-based, use a separate file.
There are plenty of formats to choose from. JSON is quite popular. You can use PHP's built-in serialization as well, which has support for more PHP-native types but is not as portable to other frameworks.
A little hacky but seems to works:
$result = eval(
'return (function() {?>' .
file_get_contents('your_article.php') .
'return [\'articleTitle\' => $articleTitle, \'heroImage\' => $heroImage];})();'
);
Where your_article.php is something like:
<?php
$articleTitle = 'hola';
$heroImage = 'como te va';
The values are returned in the $result array.
Explanation:
Build a string of php code where the code in your article scripts are wrapped inside a function that returns an array with the values you want.
function() {
//code of your article.php
return ['articleTitle' => $articleTitle, 'heroImage' => $heroImage];
}
Maybe you must do some adaptations to the strings due <?php ?> tags placements.
Anyway, this stuff is ugly. I'm very sure that it can be refactored in some way.
Your problem (probably) comes down to using parentheses with require. See the example and note here.
Instead, format your code like this
$articlesInfo = []; // watch your spelling here
foreach ($articleContents as $file) {
ob_start();
if (require '../articles/' . $file) { // note, no parentheses around the path
$articlesInfo[] = [
$file,
$articleTitle,
$heroImage
];
}
ob_end_clean();
}
Update: I've tested this and it works just fine.
I'm working on a project and I would like know which is the best way for show the data after having insert a record on the database having an complex structure html between use the structure in php or jQuery and why?.
Example of scenario:
I'm building a system of posts and comments. When the user write the post and publish it an call ajax trigger the function php and it insert the information on the database. until here all ok, but then I have to display the message insert on the wall of the user, so which the best way for display that post insert?
There are so many different way, but which is the best keeping attention on speed, security, and compatibility?
some example, suggest are welcome:
<script> // default example of call ajax
$.ajax({
type: "POST",
url: 'http://localhost/ajax_ouput_post',
dataType: 'json',
data: { id_user : 1, title : "Hello everyone", content : "Good morning" },
success: function(html) {
// output under php example
},
error: function(){
alert('Error on ajax call');
}
});
</script>
1- I create the template for the output in php.
<?php
function ajax_output_post() {
$title = $_POST['title'];
$content = $_POST['content'];
$id_user = $_POST['id_user'];
// all the check for the input
$query = // insert data on mysql #return id_post;
$select_last_post_mysql = // select the last query from the db using id_user and id_post
foreach ($select_last_post_mysql as $post) {
$template = // html structure;
$template .= // continue html structure;
if ($post->photo == 1) {
$template .= // <div>photo user</div>
}
$template .= // ecc...
}
echo json_encode(array('template' => $template));
}
?>
jquery output
<script>
$(#wall).append(html.template);
</script>
php second example with output jQuery template
<?php
function ajax_output_post() {
$title = $_POST['title'];
$content = $_POST['content'];
$id_user = $_POST['id_user'];
// all the check for the input
$query = // insert data on mysql #return id_post;
$select_last_post_mysql = // select the last query from the db using id_user and id_post
foreach ($select_last_post_mysql as $post) {
$title_json = $post->title;
$content_json = $post->content;
if ($post->photo == 1) {
$photo_user_json = $post->photo_user;
} else {
$photo_user_json = "";
}
$id_post = $post->id_post;
}
echo json_encode(array('title' => $title_json, 'content' => $content_json, 'id_post' => $id_post));
}
?>
jquery
<script>
// in jquery
var Template = {
main_container: "<div class='post_container' data-post='" + html.id_post + "'>",
title: "<div class='title'>" + html.title + "</div>",
content: "<div class='content'>" + html.content + "</div>",
close_main: "</div>",
run: function() {
return Template.main_container + Template.content + Template.close_main;
}
};
$('#wall').append(Template.run());
</script>
Well, there is not really a 'best' way to do this, it always depends on the concrete situation.
In your case you could:
simply attach the user post to the DOM via javascript, without knowing whether it was inserted to the database or not (because you have all data of the post at client side available, you do not need to select it again)
attach the user post by javascript (like in the point above) after you know it was inserted (in the success handler, but still no data in response from php)
I would recommend not to select the inserted data again anyway, except you need an auto generated value like id or inserted-timestamp, instead you could simply return the values of the request in your response after the insert.
Hope this helps.
There are a few ways to accomplish this, and there are tradeoffs to be made for each of them.
The simplest way to accomplish this is to retain the user input in javascript as answered by Nico above. The pro for this method is it is very simple to accomplish and move on to other problems and due to this simplicity, has few moving parts to be misused. The major con for this method is that it means the frontend is making blind assumptions about what is going on when the backend logic processes the data, which presents the risk of misinforming the user about what happened if the backend silently fails, and this sort of bug is often difficult to catch until it bites someone and they complain. This is probably your best answer for simple use cases, but if you intend to scale it or introduce significant complexity this makes the likelihood of bugs high and should be replaced.
As per your given example, you seem to be on this frame of thought already. So to address each of your given concerns, first I would say that compatibility is best addressed by being generally unopinionated. This is appropriate for a library meant to be used for general consumption, but is not necessarily preferable for a private project or internal business logic, which require opinion to execute a specific desired result. The major caveat in terms of compatibility is that templating is handled by the frontend at least as often as the backend. It may in some cases be done in ReactJS or Angular, or it may be done on the backend by Twig, or any number of other things. If you want wide compatibility, then this should have some configuration for whether to pass response in raw format or accompanied by a template. In the case of private business logic or an app you are building with a specific purpose, the underlying point is to accomplish a specific result, and either using the existing templating structure of the system or picking one and sticking to it is preferable so you are focusing on the end goal and not getting distracted. But either way a github library author and a app developer would likely solve this same problem in completely different ways, and neither of them are wrong.
In terms of security, the typical concerns all apply. Individual approach is mostly arbitrary, provided you cover these bases if user input is being output, entered into template content, or stored in a database.
In terms of speed, the javascript DOM option is always going to be the fastest. However you can make it almost as fast depending how much tolerance for optimization you have. You could perhaps use client side storage to cache unmodified templates client side, and just use a hash of the template content as its identifying key so it automatically flushes when you change the template on the server. If you then send the key to the server and it matches, you don't need to serve the template in the response body because the client already has the correct one. If the template hash on the backend is different, you serve the new one, bust the storage cache for that template and replace the key and value with the new one. This will make the template body, which is almost certainly the longest part of the response, only need to be sent when changes are made. You would need to inject the values into the static template clientside to do it this way, and still obtain those from the server on each request. On the backend, you do not want to make a separate SELECT statement. You want to store the values in an array and just return those if your INSERT query is successful, like maybe something like this:
<?php
// List of keys that MUST be present for the query to succeed. Don't bother calling the db if any of these did not come in the request.
$expected_keys = [
'id_user',
'title',
'content' // add whatever else has to be passed
];
// key/val for db insert
$vals = [];
try {
// verify expected keys are provided
foreach ( $expected_keys as $k => $expected ) {
if !(array_key_exists ( $expected, $_POST ) ) {
// not a valid post, return an error
throw new \InvalidArgumentException( sprintf( 'Expected field [%1$s] was not provided. ', $expected ) );
}
// retain the field in $vals for insert
$vals[ $expected ] = $_POST[$expected];
}
$dsn = "mysql:host=localhost;dbname=myDatabase;charset=utf8mb4";
$options = [
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
];
$pdo = new \PDO($dsn, "username", "password", $options);
$stmt = $pdo->prepare(
'INSERT INTO `myTable` ( `:' .
implode( '`, `:', array_keys( $vals ) ) .
'` ) VALUES (' .
implode( '", ":', $vals ) .
');'
);
$stmt->execute( $vals );
$stmt = null;
}
// User error
catch \InvalidArgumentException $e {
// return 400 Bad Request and $e->getMessage() as the error message
}
// Server error
catch \Exception $e {
// log or whatever and return a 500 error
}
// return success with whatever you need to send back from $vals or $_POST
I am developing a php website that needs to be multilingual.
For this reason, I implemented a translation function which has the following header:
function t($string, $replace_pairs = array(), $language = NULL)
Basically, this function is called like this in multiples files of my project:
echo '<p>' . t('Hello world!') . '</p>';
$hello_String = t("Hello #name!", array('#name'=>$username));
I haven't generated the translation strings yet and I would like to generate multiple translation file automatically (one for each language).
What I am looking for is a bash program (or a single command, using grep for example) that would look for every call to this t() function and generate a php file with the following structure:
<?php
/* Translation file "fr.php" */
$strings['fr']['Hello world!'] = '';
$strings['fr']['Hello #name!'] = '';
Has anyone ever encountered this situation and could help me with this ?
Thank you very much.
Kind regards,
Matthieu
Yes, you're not exactly the first to come across this. :)
You can use the venerable gettext system for this, you don't need to invent your own functions. Then you'd get to use xgettext, which is a command line utility to extract strings using the _() function.
If you want to roll your own system for whatever reason, your best bet is to write a PHP script which uses token_get_all to tokenize the source, then go through the tokens and look for T_FUNCTIONs with the value t.
No need to reinvent the wheel
Drupal uses the same t() function for its localization and the potx module is your friend.
If you don't already have, or want to install, a drupal instance you can look at the potx.inc file and reuse it in your script.
Here is the complete API documentation for the translation template extractor.
Try this script http://pastie.org/4568713
Usage:
php script.php ./proj-directory lang1 lang2 lang3
This creates lang1.php, lang2.php, lang3.php files in ./lang directory
You need two functions:
1- scan directories for php files. like this
2- match your t function, grep string and generate the language file. like
function genLang($file) {
$content = file_get_contents($file);
preg_match(...);
foreach(...){
echo(...);
}
}
Yii framework also uses same functionality,
see their MessageCommand class
https://github.com/yiisoft/yii/blob/master/framework/cli/commands/MessageCommand.php#L125
What you need is a (very simple) "template system", but there are two instances of templating in your problem.
Transform "Hello $X!" into "Hello Jonh!" or "Hello Maria!", setting $X. (PHP do this for you in string declarations).
Select the adequate template: "Hello $X!" for english, "¡Hola $X!" for spanish.
The item 1 is the more simple, but the algorithm order is 2,1 (item 2 them item 1).
For this simple task you not need a regular expression (to reinvent the "string with place-holder" of PHP).
Illustrating
For item 1, the simplest way is to declare a specialized function to say "Hello",
// for any PHP version.
function template1($name) { return "<p>Hello $name!</p>";}
print template1("Maria");
For item 2 you need a generalization, that PHP do also for you, by a closure,
header('Content-Type: text/html; charset=utf-8'); // only for remember UTF8.
// for PHP 5.3+. Use
function generalTemplate1($K) {
// $K was a literal constant, now is a customized content.
return function($name) use ($K) {return "<p>$K $name!</p>"; };
}
// Configuring template1 (T1) for each language:
$T1_en = generalTemplate1('Hello'); // english template
$T1_es = generalTemplate1('¡Hola'); // spanish template
// using the T1 multilingual
print $T1_en('Jonh'); // Hello Jonh!
print $T1_es('Maria'); // ¡Hola Maria!
For more templates, use generalTemplate2(), generalTemplate3(), etc.; $T2_en, $T2_es, $T2_fr, $T3_en, $T3_es, etc.
Solution
Now, for pratical use, you like to use arrays... Well, there are a datastructure problem,
and more 1 level of generalization. The cost is variable-name parser for place-holders. I used simple regular expression with preg_replace_callback().
function expandMultilangTemplate($T,$K,$lang,$X) {
// string $T is a template, a HTML structure with $K and $X placeholders.
// array $K is a specific language constants for the template.
// array $lang is the language, a standard 2-letter code. "en", "fr", etc.
// array $X is a set of name-value (compatible with $T placeholders).
// Parsing steps:
$T = str_replace('{#K}',$K[$lang],$T); // STEP-1: expand K into T with lang.
// STEP-2: expand X into T
global $_expMultTpl_X; // need to be global for old PHP versions
$_expMultTpl_X = $X;
$T = preg_replace_callback(
'/#([a-z]+)/',
create_function(
'$m',
'global $_expMultTpl_X;
return array_key_exists($m[1],$_expMultTpl_X)?
$_expMultTpl_X[$m[1]]:
"";
'
),
$T
);
return $T;
}
// CONFIGURING YOUR TEMPLATE AND LANGUAGES:
$T = "<p>{#K} #name#surname!</p>";
$K = array('en'=>'Hello','es'=>'¡Hola');
// take care with things like "!", that is generic, and "¡" that is not.
// USING!
print expandMultilangTemplate(
$T, $K, 'en', array('name'=>'Jonh', 'surname'=>' Smith') );
print expandMultilangTemplate($T, $K, 'es', array('name'=>'Maria'));
I tested this script with PHP5, but it runs with older (PHP 4.0.7+).
About "multilingual files": if your translations are into files, you can use somthing like
$K = getTranslation('translationFile.txt');
function getTranslation($file,$sep='|') {
$K = array();
foreach (file($file) as $line) {
list($lang,$words) = explode($sep,$line);
$K[$lang]=$words;
}
}
and a file as
en|Hello
es|¡Hola
Simplest with PHP 5.3
If you using PHP 5.3+, there are a simple and elegant way to express this "simplest multilingual template system",
function expandMultilangTemplate($T,$K,$lang,$X) {
$T = str_replace('{#K}',$K[$lang],$T);
$T = preg_replace_callback(
'/#([a-z]+)/',
function($m,$X=NULL) use ($X) {
return array_key_exists($m[1],$X)? $X[$m[1]]: '';
},
$T
);
return $T;
}
First off, I do not want what is in the URL query. I want what PHP see's in the$_GET array.
This is because the URL query will not show all the params if mod_rewrite has been used to make pretty URLs
So is there a way to get the query string that would match exactly what is in the php $_GET array?
--
I came up with a way myself using PHP and JavaScript like so:
function query_string()
{
<?php
function assoc_array_to_string ($arr)
{
$a = array();
foreach($arr as $key => $value)
{
$str = $key.'='.$value;
$a[] = $str;
}
return implode("&",$a);
}
?>
return '<?=urlencode(assoc_array_to_string($_GET))?>';
}
...but I need to do this with just javascript if possible because I can't put PHP code in a .js file.
Won't JavaScript "only see" the query string? How would client-side script know about any rewrite rules?
The only way I can think of is to use PHP -- echo it into a variable in an inline script in your main page rather than the JS file.
In your page <head>:
<script type="text/javascript">
var phpQueryParams = <?php print json_encode($_GET); ?>
</script>
Assuming at least PHP 5.2, otherwise use an external package
The query string is found in window.location.search, but that's the raw query string. So if you run something like this:
(function () {
QueryStr = {}
QueryStr.raw = window.location.search.substr(1);
var pairStrs = QueryStr.raw.split('&');
QueryStr.val = {}
for(var i=0,z=pairStrs.length; i < z; i++) {
var pair = pairStrs[i].split('=');
QueryStr.val[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
})();
You'd have something very much like $_GET in QueryStr.val.
Of course, you mention that you've mixed things up a bit using mod_rewrite, which is cool, but since we don't know your rewrite scheme, we can't help specifically with that.
However... you know your rewrite scheme, and you could probably modify the code I gave above to operate on some other part of window.location. My bet is that you'd want to split window.location.pathname on the / character instead of &.
I'm creating an open-source cms and was just wondering that which is the best way to add localizations? I already decided to have them in files similar to lang.en.php. I would assume arrays, but in which form?
$lang['xyz'] = "Text goes here!";
$lang['Text goes here!'] = "Translated text!";
Or should I create my custom parser and add localizations to a file, like this:
"Text goes here!" = "Translated text!";
And then just parse it.
What would you suggest? I tried to search but no results for me.
Martti Laine
I know the Gettext library for Desktop applications does something similar to your custom parser. Gettext has a module in PHP, but I'm not sure if it's installed in most PHP installations by default.
Basically, you would write every string with it with a function name tr("How are you?"). Then create a function to translate it:
include('lang.es.php');
function tr($txt) {
global $tr;
if(array_key_exists($txt,$tr)) {
return $tr($txt);
}
return $txt;
}
And in lang.es.php, have:
$tr = array();
$tr["How are you?"] = "¿Como Estas?";
You would probably want to do printf(tr("How are you, %s?"), $name); for variables, or proper nouns that should not be translated.
I think you should use the Joomla way. Language files must be in ini extension:
FOO=translation
BAR=translation2
then you parse the file with parse_ini_file function and get the translation array:
$dictionary=parse_ini_file("english.ini");
function translate($text)
{
global $dictionary;
if(isset($dictionary[strtoupper($text)])) return $dictionary[strtoupper($text)];
else return $text;
}
It's not as simple as you think it is, do you really need hundreds of rows in an array in order to translate I deleted 45 comments, or I deleted 192 comments? etc.
It would be very helpful if you could call a translate function with: translate('I deleted %d comments', $number);
<?php
$dict = parse_ini_file('lang.ini');
function translate($text){
global $dict;
$args = func_get_args();
if(isset($dict[$text])){
// I am not sure how to convert %d in $args[.], maybe someone else could provide a regular expression for this.
} else {
return $text;
}
}
?>
How will you manage plural form ?
Some languages have very tricky plural rules : example here
In Polish we use e.g. plik (file) this
way:
1 plik
2,3,4 pliki
5-21 pliko'w
22-24 pliki
25-31 pliko'w
For this reason, I suggest you to use gettext because everything has been done for you.