I'm trying to (in a parallel fashion, that's it, as we're still developing under PHP 5.2.x for our main applications) adapt our current applications to PHP 5.4.x (using Apache 2.4.2 with PHP 5.4.1 in our testing server) but I'm finding some warnings in our current applications when we run them in the testing server.
For example... we're using constant definition in our current applications with a function that detects the language and loads a language file containing the definitions like idiomas/lang.php, being lang a 2-digit language representation (es, us, de, and so on).
Inside each language file there's some definitions like this one:
define("IDIOMA_AF", "Afrikaans");
define("IDIOMA_AR", "العربية");
define("IDIOMA_BG", "български език");
So, when we want to output a given translated text, we do this:
<?php echo IDIOMA_AF; ?>
The message PHP 5.4.1 outputs when the application is thrown at it is the following:
Notice: Constant IDIOMA_AF already defined in D:\apache\htdocs\aplicacion\modulos\base\idiomas\es.php on line 34
Does anyone have the same problem? I would like to know a bit about your experiences, as it would be useful to know how to solve these problems. We're thinking on implementing a better system using gettext (which I think is more organized and easier to handle in the long run, but still...), although we would like to run our current applications for a while before upgrading them.
It means you are trying to define the constant twice (which is not allowed).
You could try:
if (!defined('IDIOMA_AF')) define('IDIOMA_AF', 'Afrikaans');
Or trace, and fix, why you are defining a constant twice anyway.
The function get_defined_constants() is also a useful debugging tool for issues like these.
Or, just ignore those errors: error_reporting(E_ALL & ~E_NOTICE);
Related
PSR-1 includes recommendation 2.3. Side Effects:
A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both.
Consider this example (my own) inside of a config.php file:
/**
* Parsing the database URL.
* DATABASE_URL is in the form:
* postgres://user:password#hostname:port/database
* e.g.:
* postgres://u123:pabc#ec2.eu-west-1.compute.amazonaws.com:5432/dxyz
*/
$url = parse_url(getenv('DATABASE_URL'));
define('DB_HOST', $url['host']);
define('DB_NAME', substr($url['path'], 1)); // get rid of initial slash
define('DB_USER', $url['user']);
define('DB_PASSWORD', $url['pass']);
If I do this, I'm effectively not respecting the recommendation. phpcs will, rightfully, complain about it, because of the variable:
FILE: config.php
-----------------------------------------------------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------
1 | WARNING | A file should declare new symbols (classes, functions, constants, etc.) and cause no other side
| | effects, or it should execute logic with side effects, but should not do both. The first symbol
| | is defined on line 17 and the first side effect is on line 162.
-----------------------------------------------------------------------------------------------------------------
An alternative would be this:
define('DB_HOST', parse_url(getenv('DATABASE_URL'))['host']);
define('DB_NAME', substr(parse_url(getenv('DATABASE_URL'))['path'], 1));
define('DB_USER', parse_url(getenv('DATABASE_URL'))['user']);
define('DB_PASSWORD', parse_url(getenv('DATABASE_URL'))['pass']);
No variable, no problem. But this is WET and hard to read.
I understand the recommendation is just that, and that it says "SHOULD", not "MUST". But this still bugs me… For one thing, anytime I check the file phpcs will complain about it, but report it just once per line, leaving the door open to adding more "side effects" which have no place in a config file.
I'm still new to this whole PSR thing.
Did I miss any clever way to get rid of the variable, while keeping things readable?
A corollary would be: how do serious projects, that insist on following recommendations to the letter, handle this?
1. It's fine, don't sweat it
You already mention it in your question, but this recommendation is a SHOULD and not a MUST.
If this is the only PSR-1 issue in your entire project: good job!
But your question was: how do other projects go about this?
2. Move away from defines for configuration
Global constants, when used incorrectly, are dependency magnets. They introduce coupling and make your code harder to digest. This Q&A is a very good read on why you should move away from them.
Use dependency injection instead (yes, scalar configuration constants are also dependencies).
3. Case study: Symfony
Symfony-based projects use:
either YAML (recommended) or XML configuration files to configure the dependency injection container, along with
environment variables, to set the configuration options specific to each environment in which the application should run. These env vars are defined in environment-specific .env files.
For example, to configure a Database service in a Symfony project you'd create a YAML file that contains:
services:
My\Database\Factory: # <-- the class we are configuring
arguments:
$url: '%env(DATABASE_URL)' # <-- configure the $url constructor argument
Symfony compiles this into PHP code, injecting the DATABASE_URL environment variable into the class that requires it.
You would then parse DATABASE_URL in the constructor of the My\Database\Factory class, and use the result to construct your database class.
Pros:
Configuration is separated from code
Configuration is easy to change
Configuration is easy to read
Cons:
Dependency injection and using a DI container has a learning curve and requires a change in the way you think about constructing objects.
As stated in the twelve-factors app methodology:
Apps sometimes store config as constants in the code. This is a violation of twelve-factor, which requires strict separation of config from code. Config varies substantially across deploys, code does not.
The twelve-factor app stores config in environment variables. Env vars are easy to change between deploys without changing any code
You're on the right track about best practices, you just need to correct some mistakes.
1. Use variables for environment variables
You want to use constants for things that are not. The value of the database name can vary according to the environment. It's NOT a constant, it's a (environment) variable, you should use $dbName = getenv('DB_NAME').
In contrast, the number π is a constant, it will never change and can be hardcoded.
You can have a look at the source code of open-source projects like Composer or the Symfony components, you'll see getenv() used to populate variables only.
2. Use directly the elements expected in the configuration
In your case you shouldn't use the full database URL as a single configuration item. You should instead separate each element in environment variables like DB_HOST, DB_NAME, DB_PORT, as expected by the configuration.
I have the following code:
$lang="de-AT";
$currency="3333";
$formatter = new NumberFormatter($lang, NumberFormatter::DECIMAL);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2);
echo $formatter->format($currency);
can be run in here with copy/paste:online php (though I don't know the php version of this site)
which outputs: 3.333 and is exactly what I've expected.
But on my local host with PHP 7.0.20 and PHPUnit 6.2.1 the Unit test gives me as result: 3 333.
Any idea what's new or why?
The difference you see here for the locales de-AT:
3.333
and
3 333
is just that with a bare string comparison, there is a difference.
For written language, there is not. Both forms are correct in that locale and show the same number.
However, you'd like to configure that more explicitly, for that, command the number-formatter to use the dot '.' as thousands separator (instead of using the compiled in locale information):
$formatter->setSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '.');
Configuration Data
The underlying cause is that the PHP intl extension next to the actual code ships with language data (from the CLDR project) which consists of the formatting information in form of symbols and rules. That means, that the formatting can change (as you found out in your Phpunit test) even if the extension version is the same. Derived from your example:
Output for hhvm-3.15.4 - 3.19.0, 7.2.0alpha1
3.333
1.1.0
Output for 5.6.0 - 5.6.30, 7.0.0 - 7.1.6
3 333
1.1.0
The line which is causing the space instead of the dot is:
http://unicode.org/cldr/trac/browser/trunk/common/main/de_AT.xml#L120 (#13494)
The concrete change:
http://unicode.org/cldr/trac/changeset/11798/trunk/common/main/de_AT.xml
Previously the number group symbol was taken over from parent locale which has the dot.
That is in Changeset 11798 Timestamp: 07/13/15 12:38:02 (2 years ago) - Releases take time, so this is why you see this only in the later releases of the library.
I can only stress the point that this is configuration data.
Dealing with Configuration in (Unit-) Tests
In a unit-test you normally don't want to test for that. In a configuration test, you might want to test for that. Your Phpunit-test did reveal that the expectation you had so far is not matched any longer. So this might require either a code-change (that is to make your configuration expectation working again) or a change in the test, as you were testing locale specific configuration you might have not been what you were looking for.
From your question I can imagine that both is possible. In the end, the test reflects your expectation and might have revealed a hidden dependency, here the locale configuration data.
A fix could be as little as switching the locale to "de" instead of "de-AT": https://3v4l.org/hB15n
Output for 5.6.0 - 5.6.30, hhvm-3.15.4 - 3.19.0, 7.0.0 - 7.2.0alpha1
3.333
1.1.0
But keep in mind that this would also hide the underlying issue that you might have tested for the wrong thing. So better keep the failing test as a note to ask yourself about the expectations you had and if the code does for what you have it written for.
I'm new to php and i'm using log4php with laravel.
My project structure is
->Root
->Laravel
->app
->folderx
->abc.php
->otherfolders
.
.
->vendor
->composer.json (contains log4php and laravel)
->logconfig.xml
I'm trying to initialize the logger from inside abc.php,
Logger::configure('../../../logconfig.xml');
but it gives the error message
Class 'Apache\Log4php\Hierarchy' not found
I verified that the class Hierarchy.php exists in the vendor/apache/log4php/src under Root folder. Also, if I open Logger.php and go to the line where Hierarchy in initialized and ctrl+click(in eclipse) on Hierarchy, it takes me to Hierarchy.php.
I'm trying to figure out why php is not able to find that class.
Any help/suggestions would be greatly appreciated.
Thanks
That error message shows that your PHP is looking for a namespaced class. Log4PHP has never been released until now with namespaces (up to version 2.3.0), so this is definitely weird.
If you accidentially use the development branch of the 3.0 version, I must currently suggest not to use it. It barely got any significant commits in the last year, and should be considered work in progress (very slow progress unfortunately).
I am learning how to use SWIG, and I am writing a php wrapper for a C library. The extension successfully compiles, but when I try to call the function I get this error:
php: symbol lookup error: /usr/lib/php5/20090626+lfs/fact.so: undefined symbol: fact
Your problem is probably due to a mismatch in the name of the module (see %module, or passed on the command line) and the name of the .so file you are generating.
PHP, or any system that accepts loadable binary modules, is going to make certain assumptions about the name of the entry point into the library it is trying to load. PHP seems to be assuming that the file name (fact.so) is going to contain a function called "fact".
When you run SWIG, explicitly setting the module name to "fact" will probably solve your problem. If not, posting the generated SWIG source file could help us debug your problem.
I want to use php in console mode and create an environment to test my functions.
I do not want to be forced to use a web browser and create a new file each time I want to test a function.
I want to access the function in the console and then it return the result.
How do I do this?
Update:
Perhaps I have explained this badly. I only want to see what result the function's return.
Maybe I have to learn unit testing but for the moment I only want an interactive console which allows me to test all functions one by one.
In my case I have to load the wordpress functions (I know how do it with a regular .php file and then a browser to parse the file) but i don't if it is possible to do it with php from the command line.
I have used phpsh in the past and found it very useful. Once you start it you will need to chdir() to where your files are and then obviously require() any files containing functions you need to test. You can then just test your function calls by typing them into the shell e.g. var_dump(some_function(1, 2));
I guess you've to be more specific what kind of functions exactly. Wordpress does not provide something like that out of the box, most PHP apps won't.
I also think you're calling for trouble here when such apps aren't developed in mind for such environments.
Here's an example trying to call "current_time()" from functions.php and the attempts I had to do just to realize it won't work that way:
php -r 'require "functions.php"; var_dump(current_time("mysql"));'
gives
Fatal error: Call to undefined function apply_filters() in functions.php on line 346
Trying
php -r 'require "functions.php"; require "plugin.php"; var_dump(current_time("mysql"));'
gives
Fatal error: Call to undefined function wp_cache_get() in functions.php on line 351
Trying
php -r 'require "functions.php"; require "plugin.php"; require "cache.php"; var_dump(current_time("mysql"));'
gives
Fatal error: Call to a member function get() on a non-object in cache.php on line 93
Looking at the last error in the source I see
function wp_cache_get($id, $flag = '') {
global $wp_object_cache;
return $wp_object_cache->get($id, $flag);
}
Using global variables makes testing in other environments a PITA if not impossible.
If this is not what you're trying to do, you've to be more specific/detailed in your question.
You are going to want to read up on "Unit Testing" in a generic sense and then try and apply them to PHP.
The framework you are using (if any), the style of code, and the tests you want to run are going to determine the exact methods you need to use. Only by first understanding the concept of Unit Tests and implementing them into your coding best-practices will you be able to make progress in this regard.
How about:
php -a
And if you compile php with readline support it'll be more fancy.