I came across some strange session behavior today while testing some things out. First off, I've been tasked to create a registration feature for a WordPress website. I've done a fair amount of work in WordPress in the past, but I've never done anything involving any server-side scripting languages.
I was testing out my work environment, which involves making sure that I'm able to connect to MySQL, making sure sessions are working, and basically just familiarizing myself with the setup.
When testing sessions, I did the following:
<?php
$timer = time();
$_SESSION[$timer] = $timer;
print_r($_SESSION);
?>
I was expecting on the first load of the page to see something like this:
Array
(
[1396745563] => 1396745563
)
... which is exactly what happened. On the second refresh, I wanted to see:
Array
(
[1396745563] => 1396745563
[1396745570] => 1396745570
)
... But instead, I saw:
Array
(
[1396745570] => 1396745570
)
Wow! The original session variable for timestamp 1396745563 was gone, and there was a new one in it's place.
My immediate reaction was that maybe a session_start() was missing, but that was not the case. Just to further convince myself that something was weird about this, I altered my code to look like this:
<?php
$timer = time();
$_SESSION['time_' . $timer] = $timer;
print_r($_SESSION);
?>
I thought that perhaps having a session variable with a timestamp for an index had some sort of special behavior (which might be the answer to this question for all I know).
This time, the behavior was exactly as I had expected. Each refresh of the page added on to the array print-out. After a few refreshes of the page, it looked something like this:
Array
(
[time_1396745663] => 1396745663
[time_1396745667] => 1396745667
[time_1396745671] => 1396745671
[time_1396745675] => 1396745675
[1396745570] => 1396745570
)
That oddball variable is the session data that carried over from my original attempt (I'm just being precise).
Can someone explain this extremely odd behavior? I can't think of anything obvious that I could have done that would have caused this.
Note: I doubt that this has anything to do with WordPress; I only included it to give a motive behind doing this.
The keys of $_SESSION adhere to the same rules as valid variable names in PHP. They must begin with a letter or an underscore, followed by any number of letters, numbers or underscores. Therefore, the output of time() can’t serve as a key unless it’s prefixed by at least one letter or underscore.
Add the line error_reporting(E_ALL); to your code and PHP will throw a notice like the following:
Notice: Unknown: Skipping numeric key 1396747512 in Unknown on line 0
Related
The PHP docs on session_name() say:
It should contain only alphanumeric characters; it should be short and descriptive (i.e. for users with enabled cookie warnings). ... The session name can't consist of digits only, at least one letter must be present. Otherwise a new session id is generated every time.
So it's clear you must have something non-numeric in there, but it's not quite clear what characters you can't have. The cookie spec itself denies ()<>#,;:\"/[]?={}, but that still leaves others that might be permitted but are not strictly alphanumeric. This is important because cookie security prefixes use - and _ in names like __Secure-PHPSESSID. So I had a rummage in the PHP source code at the session_name function – but I can't see that it does anything other than check it's a string. In practice, it works fine, but I'd be more comfortable knowing precisely why! For example, this works:
session_name('__Secure-PHPSESSID');
session_start();
$_SESSION['test'] = $_SESSION['test'] . "\n" . rand(0,100);
var_dump($_SESSION);
So what are the actual limits on PHP session names?
I got a bit further with this. The rules for a session name are defined in this validation function, which permits [a-zA-Z0-9,-]{1,256} (but not numeric-only). You can have commas and dashes in session names in addition to alphanumerics, so the docs are wrong on that. This function is called from an internal session_create_id function, which triggers a warning if the session name doesn't pass that validation.
Despite this, no warning is triggered when passing in a session name containing _. This is demonstrable:
<?php
ini_set('display_errors', true);
error_reporting(E_ALL);
session_name('__Secure-MySession');
session_start();
if (!array_key_exists('test', $_SESSION)) {
$_SESSION['test'] = '';
}
$_SESSION['test'] .= "\n" . rand(0,100);
var_dump($_SESSION);
echo session_name();
This works perfectly, triggering no errors or warnings, and shows a growing list of random numbers (showing that the session storage is working and therefore the cookies are too), and the second session_name call with no params shows the session name that we set:
__Secure-MySession
And the HTTP headers show that the script sets a cookie called __Secure-MySession:
I also tried naming the session My_Session, just in case PHP looks for explicit __Session- prefix, but that works just fine too. Other characters like # or ( do not trigger an error either; in those cases the session name is URL-encoded, which looks remarkably like this bug that was fixed quite a while ago. As expected, 123, works, but also URL-encodes the comma.
So while this demonstrates that having _ in session names works fine, I can't tell you why. I've asked elsewhere too, and if I find out, I will update this question!
Coincidentally, draft 06 of RFC6265bis expires today.
I've got a problem that seems particularly odd (to me at least). So I'm using a soap API to interact with a program that (among other things) creates user groups and assigns users to them. I need to create a group and then assign a user to it using two different soap functions; this requires me to store the returned variable from one soap api call and input it into the next (see below).
$value = $SOAP->addOrganisation($params);
$group=$SOAP->__getLastResponse();
echo "<br/>Group: ".$group."<br/>";
$params=array('SID'=>$sid, 'user_id'=>$user, 'organisation_id'=>$group, 'insertedby'=>1);
$value= $SOAP->addUserToOrganisation($params);
Straight-forward right?
But here's where it gets interesting: the variable $group echos what I'm expecting but seems not to work in the soap call addUserToOrganisation. The returned value says that it ran fine, however addUserToOrganisation doesn't end up carrying out its task (actually adding the user to the group).
Conjectures anticipated: (Stuff I've already guessed at)
Maybe it's a glitch in the function addUserToOrganisation?
That would make sense and I thought the same. However when I hard-coded a number in there (even when it was the exact same number the variable would have echoed), it works just fine.
Maybe the API doesn't play nicely with variables in that spot in the array?
I actually reset the variable manually with a number (see below) and it worked no problem.
$value = $SOAP->addOrganisation($params);
$group=$SOAP->__getLastResponse();
echo "<br/>Group: ".$group."<br/>";
$group=55;
echo "<br/>Group: ".$group."<br/>";
$params=array('SID'=>$sid, 'user_id'=>$user, 'organisation_id'=>$group, 'insertedby'=>1);
$value= $SOAP->addUserToOrganisation($params);
Any stray spaces attached to that variable?
None that I can see.
...Maybe you pissed off the gnomes inside your computer that make it work?
Quite possibly but they've put up with my abuse for so long, why the sudden uprising?
My best guess: As far as I can tell, there just seems that there is something inherently unacceptable about using the output of __getLastRequest(), even though it just looks like a regular old number when echoed. I don't know if not all strings are equal or something but if any of you have an idea, I'd really appreciate it.
(just watch, it turns out to somehow be some dumb syntax error)
Anyway, thanks in advance
I have a relatively high traffic site, that about once per day generates the error message:
PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0
My guess is that it is caused by some crawler that has found a URL link structure that never ends. However, I couldn't find any thing in the access logs that would be a problem (e.g. a url with a 1000+ get parameters).
It there an easy way to get insight into this Warning? It happens before any php scripts are loaded, so I assume it is impossible to introspect with php. If I had simple details such as the URL, it would be easy to solve.
If it only happens occassionally and potentially also affects end users it's actually rather safe to just raise the limit - it's a limitation imposed for practical reasons to circumvent possible attacks.
Practically, to debug this I'd dive into edge cases. In a real world scenario I'd indeed expect this error to only occur when something is nested indefinitely. I'd insert a small detection script somewhere in code that's always included, for example:
function detectLargeInputs($name, $array)
{
if(count($array) > 500)
mail('mymail#domain.tld', 'Large input in '.$name,
print_r($array, true).print_r($_SERVER, true));
}
detectLargeInputs('GET', $_GET);
detectLargeInputs('POST', $_POST);
detectLargeInputs('COOKIE', $_COOKIE);
This should show the problem within a day, including the $_SERVER info that also has fields like REQUEST_URI and HTTP_REFERER which should help you pinpoint the issue exactly.
Attention! This detection won't alert if the input vars are in an sub array:
Array
(
[foo] => 100
[bars] => Array
(
[0] => 1111
[1] => 1111
...(more than 1000)
)
)
The return value of 'count' will be 2 and everything seems ok but PHP won't handle the more than 1000 values in the sub array.
So I have a script, thats a handful of if-else statements. That if there is an error somewhere It will append a message to an array. Since there can be a good handful of errors for whatever case. I dump them in this array to later list them to the user.
$outputMsg = array();
$outputMsg[] = "State not selected";
Which this worked just fine up until today, which at random I start getting this error.
[] operator not supported for strings
I can't figure it out.
As pointed out in the comments.. upon a deeper inspection of my code, it turns out that my array is being converted into a string, and despite that notion continues on, where once upon hitting another error to append to the array, that error occurs.
I appreciate the help. But it is now resolved I believe, if not, I know what I'm looking for now. The problem was about 100 lines up from where the state error is, someone added in a new error for a new field, and didn't make it append to the array in a similar fashion, they just made it a string.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I need some PHP code to convert some PHP into JS.
functionality - I'm using common PHP functions from php.js
syntax - ???
The issue is converting the syntax. I don't need full PHP syntax, mind you; no need for supporting class definitions/declarations. Here's a small checklist of what needs conversion:
"." should be "+" (string concat)
"->" should be "." (object operator)
"::" should be "." (class operator - not really required)
Please note that the resulting code is pretty much independent of the PHP environment, so no "what if it uses a PHP class?".
I'm not asking for full code, just a tip on the right direction to this kind of conversion; I was thinking about employing a state machine/engine.
If you're curious as to why I'm pushing code to the user side: I need a dynamic way to change visibility of certain elements given certain conditions. My plan is to do this without having to execute this code server side and having unnecessary ajax calls.
Edit: Look people. I know not using AJAX sounds ludicrous to you but the world doesn't work on hype and nice-sounding design conditions (=ajax). I simply can't afford each single user polling my server 5 to 10 times per second just for my server to return a "yes" or "no" answer. Keep in mind that switching is asynchronous, and I can't buffer the AJAX calls.
Edit 2: I am sure what I'm doing is the best way in my situation. There is no "possibly better" way, so quit posting non-constructive comments. I can't get into any more detail than I have so already. The conversion from PHP code to JS is simply a matter of shortening user input; we only need one expression, then convert it to whichever language is necessary (in this particular case, from PHP to JS). The conditions on how this works will not change regardless if I describe the system down to the API specs, and inundating the topic with useless (for you) prototype docs will not help at all.
Also, for those thinking this idea came after waking up form some dream; know this has been reviewed between technical development and QA, so please do not deviate into inexistent design issues.
Edit 3: Examples (original PHP code and expected output):
(original) -- (converted)
5=="test" -- 5=="test"
'$'.(func(12)*10) -- '$'+(func(12)*10)
Fields::count()==5 -- Fields.count()==5
$this->id==5 -- this.id==5
About the last example, don't worry about context/scope, it is correct. Also note that the expressions may look weird; this is because they are expression; a single line of code that must return a value, which explains the absence of an EOL (;) and the multiple use of returning a boolean value. (exotic stuff like backtick operator execution, PHP tags, echo, die, list, etc.. left out on purpose)
Okay, let me take a stab at this one...
Screw regexes. I love them, but there's a better way, and it's built in. Check out token_get_all(). It will parse PHP source as a string and return a list of the very same tokens that PHP itself uses (including the beloved T_PAAMAYIM_NEKUDOTAYIM). You can then completely reconstruct the source of the script, one token at a time, translating it into Javascript syntax along the way.
[charles#teh ~]$ php --interactive
Interactive shell
php > print_r(token_get_all('<?php class Foo { public function bar() { echo "Yikes!"; } } $f = new Foo(); $f->bar(); ?>'));
Array
(
[0] => Array
(
[0] => 368
[1] => <?php
[2] => 1
)
[1] => Array
(
[0] => 353
[1] => class
[2] => 1
)
[2] => Array
(
[0] => 371
[1] =>
[2] => 1
)
[3] => Array
(
[0] => 307
[1] => Foo
[2] => 1
)
...
While this may be a bit overkill, it also uses the same parsing rules PHP uses, and should therefore be less of a long-term pain than regular expressions. It also gives you the flexibility to detect features that can't be translated (i.e. things that php-js doesn't support) and reject the translation and/or work around the problem.
Also, you still haven't told us what you're doing and why you're doing it. There are still probably more accurate, useful answers available. Help us help you by giving us more information.
You believe polling to be unrealistic due to an expected stupidly high number of requests per second. Why are you expecting that number? What does your application do that would cause such conditions?
Why do you want to translate PHP code rather than writing specific Javascript? You're just manipulating page contents a bit, why do you need PHP code to make that decision?
Language translation is probably the least simple solution to this problem, and is therefore an amazingly awful idea. It couldn't have been arrived at as the first option. What are your other options, and why have they been ruled out?
Have you tried Harmony Framework?
Here's the quick and dirty solution I came up with, written in under 20 minutes (probably lots of bugs), but it looks like it works.
function convertPhpToJs($php){
$php=str_split($php,1); $js='';
$str=''; // state; either empty or a quote character
$strs=array('\'','`','"'); // string quotes; single double and backtick
$nums=array('0','1','2','3','4','5','6','7','8','9'); // numerals
$wsps=array(chr(9),chr(10),chr(13),chr(32)); // remove whitespace from code
foreach($php as $n=>$c){
$p=isset($php[$n-1])?$php[$n-1]:'';
$f=isset($php[$n+1])?$php[$n+1]:'';
if($str!='' && $str!=$c){ $js.=$c; continue; } // in a string
if($str=='' && in_array($c,$strs)){ $str=$c; $js.=$c; continue; } // starting a string
if($str!='' && $str==$c){ $str=''; $js.=$c; continue; } // ending a string
// else, it is inside code
if($c=='$')continue; // filter out perl-style variable names
if($c==':' && $f==':'){ $js.='.'; continue; } // replace 1st of :: to .
if($p==':' && $c==':')continue; // filter out 2nd char of ::
if($c=='-' && $f=='>'){ $js.='.'; continue; } // replace 1st of -> to .
if($p=='-' && $c=='>')continue; // filter out 2nd char of ->
if($c=='.' && (!in_array($p,$nums) || !in_array($f,$nums))){ $js.='+'; continue; } // replace string concat op . to +
if(in_array($c,$wsps))continue; // filter out whitespace
$js.=$c;
}
return $js;
}
The following:
$window->alert("$".Math::round(450/10));
Converted to:
window.alert("$"+Math.round(450/10));
Edit: Can't believe all the fuss this question caused compared to the time taken.
Feel free to criticize at will. I don't actually like it much personally.
I wrote a tool called php2js that can automatically convert PHP code to javascript. It is not perfect, but supports the most common PHP functionality including classes, inheritance, arrays, etc, etc. It also includes and knows about php.js, so code written with php's standard library functions may "just work".
Maybe you will find it useful.
Im created tool PHP-to-JavaScript for converting PHP code to JavaScript. Its support:
Namespaces,use
Class, abstract class extends and interfaces
constants and define
Exceptions and catch
list()
magic methods __get __set and __call
and more