Discovered through a necessary change for the new kernel: #1536844: Port language/path handling to the kernel model

The check for unicode support as well as the gpc_magic fix could and should happen way earlier in the bootstrap. Both don't seem to have any dependencies on other things.

Also, fix_gpc_magic() uses ini_get(), even though there's a utility function get_magic_quotes_gpc() since PHP4.

btw, the gpc_magic fix is no longer required with PHP 5.4.

@todo The installer probably contains a special unicode check somewhere that manually loads it before attempting to bootstrap.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

1) At minimum, there's array_walk_recursive() since PHP5.

2) Since stripslashes() does not alter $item by reference, an anonymous function might be even better.

3) In general, I wonder whether we're able to come up with some more performant code for recursing into these array keys and adjusting their values.

4) Another option would be to just simply outright deny running Drupal on hosts where magic_quotes_gpc is enabled.

Crell’s picture

I am fully in support of option 4. magic_quotes have been disabled by default in PHP since before any of us were even using Drupal. It's explicitly deprecated in 5.3. It doesn't even exist in 5.4, and by the time D8 ships PHP 5.3 will be on long-term-security-fix-only mode it will be so old.

Plus our htaccess file already disables it. You really have to go out of your way to have magic_quotes on. Let's just cut to the chase and either ignore it entirely or hard-fail.

Crell’s picture

Follow-up: I believe we already outright refuse to run if register_globals are enabled. magic_quotes are in the same family of PHP 3-era dumb.

sun’s picture

Alright, let's remove that BC layer and bail out entirely.

Status: Needs review » Needs work

The last submitted patch, drupal8.unicode-magic-quotes.4.patch, failed testing.

effulgentsia’s picture

+1 to moving unicode_check() earlier and removing fix_gpc_magic().

+++ b/core/includes/bootstrap.inc
@@ -2164,6 +2169,11 @@ function _drupal_bootstrap_configuration() {
+
+  // Detect string handling method.
+  require_once DRUPAL_ROOT . '/core/includes/unicode.inc';
+  unicode_check();
+

Any reason not to move this into drupal_environment_initialize() which already ends with a setlocale() function, so this would logically follow? Only potential downside to that that I can think of is that it would then happen before the page timer starts, but I also wonder if we could avoid including unicode.inc this early and instead move unicode_check() to bootstrap.inc. Also, we can pass a parameter to _unicode_check() (which is already incorrectly documented as taking one) to avoid running t() when at this calling point, we don't use the result anyway. At which point, there's nothing time consuming happening here, so it's okay for it to happen before the page timer starts.

effulgentsia’s picture

Title: Move unicode check and gpc_magic fix earlier in bootstrap » Move unicode check earlier in bootstrap and replace fix_gpc_magic() with a hard requirement for magic quotes to be disabled
sun’s picture

Status: Needs work » Needs review
sun’s picture

sun’s picture

effulgentsia’s picture

Status: Needs review » Needs work

As per #1541990-9: Improve check before calling t() (D8 drupal_container() patch broke drush), this will currently fatal error if t() is called from a failing _unicode_check() condition, because t() fatal errors if called before drupal_classloader() (which itself depends on DRUPAL_BOOTSTRAP_CONFIGURATION). Moshe asked in that other issue if we can make t() or drupal_container() robust enough to not fatal error when called before we have a class loader. We can, but I don't know if we should. If Drupal 8 is increasingly moving to autoloaded classes, I don't know if we want to start having lots of procedural wrappers containing code that branches on whether we have an autoloader.

For this issue, the return values from t() aren't used anyway (from this particular check at this particular point in bootstrap), so we can sidestep the problem by having unicode_check() pass _unicode_check() a parameter to not return error strings. But, maybe this is a good issue in which to discuss whether t() and other bootstrap functions that use classes should be made to worry about the possibility of running before there's an autoloader.

Crell’s picture

IMO, anything that happens before the class loader is enabled is caveat emptor. Nothing but hard-coded core initialization code should do so, and we should keep that to an absolute minimum. Currently install.php, update.php, etc. violate that because they're one off dedicated scripts. Fixing that is a separate issue.

So no, we should not special case things for when the class loader doesn't exist yet. We should just make sure nothing non-trivial happens before there is a class loader.

sun’s picture

The regular runtime t() depends on way more than just configuration. It requires the full language subsystem to be loaded and initialized, which in turn requires the module system.

The alternative result of get_t(), st(), however, should work fine, as it's designed to operate in an environment that acts with .po gettext files.

Thus, unless we can output English strings or find a conditional solution, unicode_check() has to be moved back into DRUPAL_BOOTSTRAP_FULL.

That said, #1542144: Mark strings as localizable/translatable (new t()-alike string function that isn't t(), only for potx) would be an entirely different approach, but would require a massive API change to hook_requirements() so that all translatable strings within all hook implementations are only marked as translatable, but only ever sent through t() upon actual output.

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
8.58 KB

For this issue, the return values from t() aren't used anyway (from this particular check at this particular point in bootstrap), so we can sidestep the problem by having unicode_check() pass _unicode_check() a parameter to not return error strings.

Just for clarity, here's what I mean.

sun’s picture

#14 could be prettier, but works for me.

moshe weitzman’s picture

re to @sun in #15: my request is not for t() to actually work very early in the bootstrap. My request is for it to return an untranslated string instead of a fatal error. Thats what we finally accomplished in D7, and Caeveat Emptor regresses us back to darker days.

Edit: I'm suggesting that t() add a try/catch and then catch an exception when language system is not available and return untranslated string in that case.

effulgentsia’s picture

IMO, anything that happens before the class loader is enabled is caveat emptor.

If we agree with this, we can help make it clear with #1593826: Split bootstrap.inc into functions available before/by settings.php and functions available after there's a class loader.

tstoeckler’s picture

Per the OP, what about get_magic_quotes_gpc() and get_magic_quotes_runtime()?

sun’s picture

Let's fix this properly. The entire get_t() and whatnot in _unicode_check() is superfluous in the first place.

effulgentsia’s picture

I like #19.

+++ b/core/includes/bootstrap.inc
@@ -577,6 +582,10 @@ function drupal_environment_initialize() {
+  require_once DRUPAL_ROOT . '/core/includes/unicode.inc';

This patch moves the 3 constants and 2 functions into bootstrap.inc rather than requiring the entire file to be loaded for cached pages.

+++ b/core/includes/unicode.inc
@@ -141,14 +142,33 @@ function unicode_requirements() {
+    case 'mbstring.func_overload':
+      $requirements['unicode']['description'] = $t('Multibyte string function overloading in PHP is active and must be disabled. Check the php.ini <em>mbstring.func_overload</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', array('@url' => 'http://www.php.net/mbstring'));

This patch changes this one to use $t_args to match the others.

effulgentsia’s picture

With the patch this time.

sun’s picture

Why not go a step further then...

sun’s picture

whoopsie ;)

effulgentsia’s picture

Status: Needs review » Reviewed & tested by the community

I like it. At first, I was concerned about #23 introducing the side-effect of setting the global as a result of calling unicode_requirements(), but it's already the case that doing that had the side-effect of possibly calling mb_internal_encoding() and mb_language() so #23 doesn't create a new side-effect, but makes the existing one consistent. A future issue can deal with removing the side-effect entirely if we want better unit testability of unicode_requirements() (which is already challenged, however, by the ini_get() calls), but that's no reason to hold up this issue, so RTBC.

neclimdul’s picture

Status: Reviewed & tested by the community » Needs review

I'm not convinced its not creating a new side effect. Its exacerbating a side effect by adding a global for the system to interact with which makes things less testable. Is there actually a reason we're doing it because I don't see the point.

sun’s picture

There is no new global variable added here. It already exists.

effulgentsia’s picture

#23 doesn't change what unicode_check() does with respect to the global, but it does change what unicode_requirements() does with respect to it. However, as per #24, I think that's okay, because even in HEAD, unicode_requirements() has the side effects mentioned, so I actually think it's a current bug for it to have that without also setting the global for consistency. @neclimdul, please RTBC or say if you still disagree. If this is a sticking point, we can go back to #21, and work through this point in a follow-up.

sun’s picture

I also want to point out that a global might be ugly, but it's used by all the unicode/mbstring PHP wrapper functions in unicode.inc. Which, in turn, are called plenty of times during runtime. Thus, changing the global to something else would require benchmarks.

The actual value of the global is platform-specific and thus cannot change on repetitive calls to unicode_check().

Furthermore, we already have unit tests for the different states of the global:
http://drupalcode.org/project/drupal.git/blob/refs/heads/8.x:/core/modul...

Lacking a proper identifier provided by PHP core, I would agree that a globally defined constant would actually be more appropriate than a global variable, but alas, a constant would break the testability of the multibyte PHP wrapper functions.

In any case, changing the global variable into something different is out of scope for this issue.

neclimdul’s picture

Status: Needs review » Reviewed & tested by the community

Gotcha, thanks for clearing that up. That was my only concern so lets do it.

catch’s picture

Title: Move unicode check earlier in bootstrap and replace fix_gpc_magic() with a hard requirement for magic quotes to be disabled » Change notification for: Move replace fix_gpc_magic() with a hard requirement for magic quotes to be disabled
Priority: Normal » Critical
Status: Reviewed & tested by the community » Active

Yeah this looks good to me, and while the global's not ideal, I agree a conditionally defined constant just to avoid setting the global would be worse. I don't see another solution coming through without a tonne of refactoring.

Committed/pushed to 8.x.

We should add a change notice and/or update http://drupal.org/requirements for this change in addition to the CHANGELOG.txt note.

webchick’s picture

Title: Change notification for: Move replace fix_gpc_magic() with a hard requirement for magic quotes to be disabled » Move replace fix_gpc_magic() with a hard requirement for magic quotes to be disabled
Priority: Critical » Normal
Status: Active » Fixed

I added a change notice at http://drupal.org/node/1600010 for both this issue and #576508: Require PHP 5.3, which can hopefully act as a general placeholder for such info that can be moved to http://drupal.org/requirements when the time comes.

webchick’s picture

Also, this change broke drush in stock MAMP. :( Sad panda.

[Edited to clarify what's broke]

effulgentsia’s picture

Details please? I'm running stock MAMP and do not see anything noticeably broken.

webchick’s picture

Sorry, that was lazy.

When you attempt to use drush, you'll get:

$ drush en statsitics
PHP's 'magic_quotes_gpc' and 'magic_quotes_runtime' settings are not supported and must be disabled.Drush command terminated abnormally due to an unrecoverable error.   [error]

It's confusing because you can install from the UI just fine. Presumably because that's got Drupal's .htaccess or whatever forcing it to false.

It's also confusing because the only reference in MAMP's php.ini file is to magic_quotes_sybase or something and it's set to Off. There is no just plain "magic_quotes_gpc" variable. However, if you run php -i | grep magic from the command-line, you'll see that it does have it set, and set to On. To fix, you'll need to edit your php.ini file, manually add an entry and set it to Off.

effulgentsia’s picture

Status: Fixed » Needs work

You really have to go out of your way to have magic_quotes on.

I can confirm #34, so this assumption was wrong. Running drush in stock MAMP is not exactly going out of your way.

From http://www.php.net/manual/en/security.magicquotes.what.php:

This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.

magic_quotes_gpc Affects HTTP Request data (GET, POST, and COOKIE). Cannot be set at runtime, and defaults to on in PHP.

If I'm reading those two statements correctly, then it's not just MAMP, but that PHP 5.3 in general has a deprecated feature enabled by default. Lovely.

sun’s picture

On Windows, PHP ships with this disabled:

; Default Value: On
; Development Value: Off
; Production Value: Off
; http://php.net/magic-quotes-gpc
magic_quotes_gpc = Off

; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
; http://php.net/magic-quotes-runtime
magic_quotes_runtime = Off

; Use Sybase-style magic quotes (escape ' with '' instead of \').
; http://php.net/magic-quotes-sybase
magic_quotes_sybase = Off
Crell’s picture

Wait a minute, let me make sure I understand.

Any sane host or distribution has, for a decade, had magic quotes off, or made it possible to turn off.

Magic quotes no longer exists in the current stable version of PHP. Its imminent demise has been known for years.

We have an .htaccess file that turns magic quotes off, assuming .htaccess files are parsed.

... and yet MAMP still has it on? Even with the .htaccess file? Meaning MAMP is both misconfigured by default and doesn't let us configure it safely?

Am I understanding this right? If so, someone needs to explain to me why we don't just file a bug upstream in MAMP and tell them to fix their default configuration, because it is already broken. We already refuse to run period for register_globals. If MAMP can't default to a sane configuration, that's their problem, IMO.

catch’s picture

@Crell:

... and yet MAMP still has it on? Even with the .htaccess file?

webchick said:

When you attempt to use drush, you'll get:

(emphasis added).

Upstream MAMP bug along with a http://drupal.org/requirements update seems enough for me though yes.

moshe weitzman’s picture

Status: Needs work » Fixed

I agree with last few posts. Not a drupal or drush issue. Back to fixed.

Damien Tournoud’s picture

This is just a case of PHP insanity. At least on PHP 5.3.8 (shipped with OS X 10.6), magic_quotes_gpc is deprecated, but it defaults to "on" when PHP has no configuration file :)

php -r 'echo ini_get("magic_quotes_gpc");'
> 1

Not sure if it got fixed later on, but we are going to need to workaround this one way or another.

sun’s picture

Hrm. Contrary to MAMP, that's a distro. What's its value with the default configuration file?

---

So what now? Add back the fix_gpc_magic() functions? (but keeping the other changes)

In case it's not clear for everyone, http://php.net/manual/info.configuration.php#ini.magic-quotes-gpc can only be set in php.ini, .htaccess or httpd.conf.

catch’s picture

Status: Fixed » Active

Moving back to active.

I had a quick look on google, there are several "how do I disable this on MAMP?" results but couldn't find a bug report against MAMP for this.

Seems worth a PHP bug report as well, would be good if they eventually fix the default in 5.3.x.

If we've filed those two, I think that's the best we can do and we should move this back to fixed.

I'm really not keen on reverting the patch, disabling it in php.ini isn't hard if you have control over your webserver. And if you don't have control and it's set to on, then it's time to get a new host.

Damien Tournoud’s picture

Status: Active » Fixed

I think people have seen that with MAMP because they are probably using the default PHP binary from the OS X system.

I think Drush is going to have to workaround this in its wrapper script.

Damien Tournoud’s picture

Status: Fixed » Active
sun’s picture

Status: Active » Fixed
sun’s picture

btw, we might want to consider to apply this snippet to D7, as it involves no callbacks and array_*() functions:
http://www.php.net/manual/en/security.magicquotes.disabling.php

sun’s picture

Rasmus clarified that it cannot be disabled by default for PHP 5.3.

@webchick: Can you try to file an issue against MAMP to fix their php.ini for CLI?

webchick’s picture

Nope. Hopefully someone else can.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

YesCT’s picture

To get drush to work, I added lines for runtime and gpc to explicitly turn them off. And found http://mediatribe.net/en/node/61#comment-808 helpful.

; Magic quotes
;

; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = Off
magic_quotes_runtime = Off
magic_quotes_gpc = Off

http://drupal.org/requirements says to do as much. But I didn't think to look there until I had googled the drush error

> drush status
PHP's 'magic_quotes_gpc' and 'magic_quotes_runtime' settings are not supported and must be disabled.Drush command terminated abnormally due to an unrecoverable [error]
error.

and found this issue and read through it. The site installed ok, and I did not run into the error until I tried to run drush status on the command line. That might lead me to look in the drush readme file, but it does not mention the magic settings.

Cameron Tod’s picture

I have reported an issue on the MAMP bug tracker. The tracker requires registration, and given the activity level I don't hold out much hope of it being addressed quickly. :/

babruix’s picture

I had same problem in MAMP, edited php.ini and restarted server, but magic_quotes_sybase had old value.
After investigation found that it uses different php.ini for command line:
echo "<?php phpinfo();" | php

Configuration File (php.ini) Path => /etc
Loaded Configuration File => (none)

So what I did is just copied edited php.ini to /etc/ like this:
sudo cp /Applications/MAMP/bin/php/php5.3.14/conf/php.ini /etc/php.ini
Then after restart magic_quotes_sybase become OFF in command line as well and drush works!
Hope that help someone.