I want to analize a source code (with token_get_all()) make some modification (for example, turning all private to public, get rid of final, etc), and save the source code but keeping the same formatting, tabulators, etc. Is there any way, to do this?
I made this code:
private static function convertSourceCodeToTestable ($sourceCodePath)
{
$newCode = '';
foreach (token_get_all(file_get_contents($sourceCodePath)) as $item)
{
if (is_array($item))
{
switch ($item[0])
{
case T_FINAL :
case T_STATIC :
$item[1] = '';
break;
case T_PRIVATE :
case T_PROTECTED :
$item[1] = 'public';
break;
}
$item = $item[1];
}
$newCode.= $item;
}
return $newCode;
}
this looks do what I want and wont mess up the code
You can generate the new file using the values in the tokens. No formatting required. Just stitch them together into a string and save it to a file. Just be sure to use is_string() to recognize whether the token is an array or just a string.
No. Formatting largely* goes out the window when doing lexical analysis. It is irrelevant to the logical structure and not preserved. You could reformat those tokens into new source code according to your favourite formatting rules, but you cannot preserve the original source formatting this way.
* There's the T_WHITESPACE token which is parsed and returned, however it doesn't look like all whitespace turns into tokens under all circumstances, so you'll probably lose at least some.
If you'd want to do this, you'd either have to write your own tokeniser/parser which also remembers the formatting, or you need to somehow reverse engineer your changes into the source code and modify the original files directly (which sounds like madness, IMO).
Related
I've got 2 files (settings1.php and settings1.default.php) that contain settings variables for a website. When the site's files are updated, its pushed to Github so others can download and use the template site too. Obviously I don't want anyone to download my settings1.php file that contains MySQL database info and other personal info, and I don't want settings1.php to be wiped out when doing a git pull, so I have settings1.default.php that I update as I add new features.
An excerpt of the file is:
<?php
$apikey = "11111111111111";
$TZ = "Europe/London";
$UTC = "1";
$lon = 0.2;
$lat = 50.1;
?>
My goal is to have a PHP script that (quickly) checks if each variable in settings1.default.php exists within settings1.php, and for each variable that doesn't exist, it copies that variable over including its default value. And if the entire settings1.php file is missing, it copies settings1.default.php to settings1.php. The script would need to contend with users adding blank lines between variables, or reordering the variables.
I've hacked out a script that, in a very messy way, does this, but only if there are no blank lines, and only if every variable is in the exact right order. Its a mess and probably very inefficient, so it'd be best to start over from scratch with the help of people that know PHP way better than I do.
As others said use an array, but I would do it this way
//settings1.default.php
<?php
return [
'apikey' => "11111111111111",
'TZ' => "Europe/London";
'UTC' => "1";
'lon' => 0.2;
'lat' => 50.1;
];
Then when you load them you can do it this way:
$default = require 'settings1.default.php';
$settings = require 'settings1.php';
//you could also use array_replace_recursive() but I would avoid array_merge_recursive()
$settings = array_merge($default, $settings);
And then if you want you can just use the merged arrays or, you can save that to a file like this:
file_put_contents('settings1.php', '<?php return '.var_export($settings, true).';');
Var Export returns a syntactically correct array, and the second argument returns it as a string rather then outputting it. Then you just need to add the <?php tag, the return and the closing ; to have PHP file.
This also keeps your variable space clean, if you add a variable, you have no grantee that someone using this does not already have that variable in use. If they don't follow best practices in there code, there is the potential to overwrite data from one or both with unknown consequences. It would also be a bit hard to debug an issue like that.
Your way
It's possible to do what you want without doing this, but you will have to include the files in a function (so you have a clean scope), call get_defined_vars then merge the 2 arrays, then loop over that and manually create the PHP for the file.
If you don't use get_defined_vars in a "clean" scope you risk importing other variables into your settings files. This could lead to security issues as well. What I mean by clean scope (just to be clear) is that within an empty function, the only variables that are defined are the ones used as arguments of the function (something we should all agree on). So by doing this we can easily remove the only "non-setting" variable, which is the filename. This way we can avoid a lot of those issues. Without doing that you really have no way to know what all would be included in get_defined_vars();, or at least I don't have any idea what could leak into it.
function loadSettings($file){
require $file;
unset($file); //remove file var you cant use this variable name in either file
return get_defined_vars();
}
$default = loadSettings('settings1.default.php');
$settings = loadSettings('settings1.php');
//you could also use array_replace_recursive() but I would avoid array_merge_recursive()
$settings = array_merge($default, $settings);
$code = '<?php'."\n";
foreach($settings AS $var => $value){
/// ${var} = "{value}";\n
$code .= '$'.$var.' = "'.$value.'"'.";\n"; //some issues with quotes on strings .. and typing here (no objects etc)
}
file_put_contents('settings1.php', $code);
Which just seems like a mess to me. Please note that you will have issues with the quotes on strings etc.. For example in the above code if the value of $value is 'some "string" here', this would create this line in the file $foo = "some "string" here"; which is a syntax error. Var Export automatically escapes these things for you. Not to mention what happens if you try to put an array in that value... Etc. Frankly I am to lazy to try to work out all the edge cases for that. Its what I like to call a naive implementation, because at first it looks easy but as you really get into it you have all these edge cases pop up.
Probably the only way to really fix that is to use var_export on each line like this:
//...
foreach($settings AS $var => $value){
/// ${var} = "{value}";\n
$code .= '$'.$var.' = '.var_export($value,true).";\n";
}
//...
You could try to do things like check if its an array, escape any non-escaped " some more type checking etc. But whats the point as you may find other edge cases later etc.. Just a headache. Var Export does a fantastic job of escaping the, and if you compare the two versions of the code, it should be clear why I made the original suggestion first.
One last note, is you can probably simplify the mixed quotes ' and ". I am just in a habit of doing everything that is PHP code in ' because you can get into problems with this stuff.
//don't do this
$code .= "$$var = ".var_export($value,true).";\n";
//or this
$code .= "${$var} = ".var_export($value,true).";\n";
As PHP will think that is code and not a string. Well it will see it as a variable variable, not something you want.
UPDATE
Based on your comment
Ended up using the second method you came up with. Also, figured out that if i replaced require with include inside the function, it would return an empty array, so the settings1.php file gets created with default values if it doesn't exist. Only thing I still need to work out is the easiest way to require settings1.default.php but not require settings1.php
I would make this small change:
function loadSettings($file){
if(!file_exists($file)) return []; //add this line
require $file;
unset($file); //remove file var you cant use this variable name in either file
return get_defined_vars();
}
While include has the behaviour you want, its not really the right thing to use here as you mentioned
I still need to work out is the easiest way to require settings1.default.php but not require settings1.php
This breaks the commonality between them, which means we have to write more code with little or no benefit. We can avoid that by adding a check in. The only difference here is it makes using require vs include a bit pointless, and both files would return an empty array if they don't exist. I would still use require because it means this code is important when you glance at it, rather or not it really does anything.
The part in bold above we can actually fix very easily by doing this:
function loadSettings($file){
if(basename($file) != 'settings1.default.php' && !file_exists($file)) return []; //add this line
require $file;
unset($file); //remove file var you cant use this variable name in either file
return get_defined_vars();
}
I left checking for the files orignally because I thought both file would always exist, and be required. Adding the filename in there also adds a hard dependency on the setting file name. If that file name changed you would have to change the code. So i wanted to add it separate, as the other behaviour may be fine.
Base name gets the trailing part of the path to avoid issues with full paths to the file, this will return the filename. Which makes matching it to our string of the filename a bit easier as we don't have to worry about the path.
So if the basename of the file is not equal to our settings.default file and the file does not exist return an array. Then when the file is equal to that file it will bail out of the if condition and go to the require part rather or not the settings.default file exits. Basically this will treat settings.default as a require, but still return an array on other files if they don't exist (like an include ).
Cheers!
I would just store all of the variables in an array, then use extract to convert them to the actual variables, and use array_intersect_key to see if they length of the intersection of the arrays is the same as the length of $defaultSettings:
//settings1.default.php
$defaultSettings = [
'apikey' => "11111111111111",
'TZ' => "Europe/London";
'UTC' => "1";
'lon' => 0.2;
'lat' => 50.1;
];
//settings.php
$settings = [
'apikey' => "11111111111111",
'TZ' => "Europe/London";
'UTC' => "1";
'lon' => 0.2;
'lat' => 50.1;
];
extract($settings); // creates the variables $apikey, $TZ, etc.
//check.php
require('settings1.default.php')
require('settings.php')
//if they don't have the same keys
if (count(array_intersect_key($settings , $defaultSettings)) !== count($defaultSettings)) {
throw new \Exception("Invalid configuration");
}
You have to include settings1.default.php in settings1.php and then have to call get_defined_vars() which will have list of vars defined in settings1.default.php then if some variable does not exist in that array build additional config values in settings1.php.
settings1.php.
<?php
include_once('settings1.default.php');
$varialbes_defined_settings1_default = get_defined_vars();
?>
This question already has answers here:
What's an actual use of variable variables?
(6 answers)
Closed 1 year ago.
I was just recently introduced to the concept of dynamic variables in PHP. To me it seems like using dynamic variables in a program would make it difficult to read/follow.
Can anyone explain to me the benefits and/or share a real simple real world example of when a dynamic variable in PHP might be needed?
If your wondering what dynamic variables are and how they work check out this question
I had voted to close this question (vote since retracted) on the basis of it being subjective, but on reflection, I think I can give an objective answer.
A static variable name is a sequence of characters, representing a token which the underlying engine uses as a label to identify the value the variable represents (very very layperson's description).
A "sequence of characters" is a string. A string is an expression that represents a string. So from there it stands to reason that any expression that represents a string ought to be good enough to represent the token that refers to a variable. And that expression itself could be assigned to a variable, and from there one gets dynamic variable names.
But this is not what you asked. You asked: why?
It's not for the implementors of the language to answer questions like that. It's their job to provide a uniform and predictable programming interface, via their language. It's uniform to be able to represent a sequence of characters via an expression which in turn could be represented by a variable. Job done.
Subjectively, I could potentially see where some data is imported from an external source, and even the schema of the data is dynamic. One might want to represent that in some sort of generic object fashion, and it leads from there that the names of the properties of the object might also be dynamic. Whether or not this might be a good approach to the problem at hand is entirely subjective, and down to the developer's judgement, and that of their peers during code review.
Another example might be that you've inherited some shoddy spaghetti code where "needs must" and using dynamic naming - for whatever reason - might be a good approach.
PHP's burden ends at providing the mechanism to write the code; it does not speak to the quality of the design of said code. That's what code review is for.
A variable variable, is just another form of reflection. You are basically asking "Why would you ever want to change a variable, if you don't know it before runtime".
Although technically not the same, you could see a variable variable as a different kind of hash table (or array in php). Most variable variables could be rewritten as a hash table, and you would not be surprised. But if you need to work with a variable before and after runtime, a hash table might even be more awful to work with.
A simple use case might be settings that can be changed by a user. Keep in mind the following example is insecure as is, but demonstrates it's purpose.
<?php
/*
Simple way, if you have a limited amount of settings
*/
$settings = array();
$settings["allowAccess"] = 1;
$settings["allowModify"] = 1;
$settings["allowDelete"] = 0;
if ($result = $mysqli->query("SELECT `allowAccess`, `allowModify`, `allowDelete` FROM `user_settings` LIMIT 1"))
{
$row = $result->fetch_array(MYSQLI_ASSOC);
$settings["allowAccess"] = $row["allowAccess"];
$settings["allowModify"] = $row["allowModify"];
$settings["allowDelete"] = $row["allowDelete"];
}
/*
Now consider you have a thousand settings and you dont want to write out every setting manually.
*/
if ($result = $mysqli->query("SELECT * FROM `user_settings` LIMIT 1"))
{
$row = $result->fetch_array(MYSQLI_ASSOC);
foreach($row as $key => $val) {
$settings[$key] = $val;
}
}
/*
Both options work, but everytime you want to use a setting you have to use something like below
*/
if ($settings["allowAccess"] && $settings["allowModify"] && $settings["allowDelete"]) {
unlink($somefile);
}
/*
Perhaps you would rather write
*/
if ($allowAccess && $allowModify && $allowDelete) {
unlink($somefile);
}
/*
Then you can use
*/
if ($result = $mysqli->query("SELECT * FROM `user_settings` LIMIT 1"))
{
$row = $result->fetch_array(MYSQLI_ASSOC);
foreach($row as $key => $val) {
//if you use extract, all columns are extracted, unless you drop them first. But perhaps you need those for something else.
//now I extract the columns that start with allow and keep the columns like id, created, modified, etc. without the need to specify each column manually, which makes it easier if you ever decide to add another setting column. You don't need to update this part of the code.
if (substr($key,0,5)=='allow') {
$$key = $val; //$key = 'allowAccess', $$key == $allowAccess = $val;
}
}
}
?>
This is just one example, I found another example in the XHTML sanitizer for MediaWiki by Brion Vibber. He uses a lot of arrays in his code and at one point he needed to flip them all. He used the code below:
<?php
$vars = array( 'htmlpairs', 'htmlsingle', 'htmlsingleonly', 'htmlnest', 'tabletags',
'htmllist', 'listtags', 'htmlsingleallowed', 'htmlelements' );
foreach ( $vars as $var ) {
$$var = array_flip( $$var );
}
?>
Now obviously he could have written the code below, but is that really easier to read?
<?php
$htmlpairs = array_flip($htmlpairs);
$htmlsingle = array_flip($htmlsingle);
$htmlsingleonly = array_flip($htmlsingleonly);
$htmlnest = array_flip($htmlnest);
$tabletags = array_flip($tabletags);
$htmllist = array_flip($htmllist);
$listtags = array_flip($listtags);
$htmlsingleallowed = array_flip($htmlsingleallowed);
$htmlelements = array_flip($htmlelements);
?>
Which also introduces another use case: What if I wanted to dynamically decide which arrays to flip? In the variable variable way, I can just push items onto the array and flip them when the time comes, In the "normal" way I would need a switch or if to loop through the array and then add each option manually.
I've got a simple question:
When is it best to sanitize user input?
And which one of these is considered the best practice:
Sanitize data before writing to database.
Save raw data and sanitize it in the view.
For example use HTML::entities() and save result to database.
Or by using HTML methods in the views because in this case laravel by default uses HTML::entities().
Or maybe by using the both.
EDIT: I found interesting example http://forums.laravel.com/viewtopic.php?id=1789. Are there other ways to solve this?
I would say you need both locations but for different reasons. When data comes in you should validate the data according to the domain, and reject requests that do not comply. As an example, there is no point in allowing a tag (or text for that matter) if you expect a number. For a parameter representing.a year, you may even want to check that it is within some range.
Sanitization kicks in for free text fields. You can still do simple validation for unexpected characters like 0-bytes. IMHO it's best to store raw through safe sql (parameterized queries) and then correctly encode for output. There are two reasons. The first is that if your sanitizer has a bug, what do you do with all the data in your database? Resanitizing can have unwanted consequences. Secondly you want to do contextual escaping, for whichever output you are using (JSON, HTML, HTML attributes etc.)
I have a full article on input filtering in Laravel, you might find it useful http://usman.it/xss-filter-laravel/, here is the excerpt from this article:
You can do a global XSS clean yourself, if you don’t have a library to write common methods you may need frequently then I ask you to create a new library Common in application/library. Put this two methods in your Common library:
/*
* Method to strip tags globally.
*/
public static function global_xss_clean()
{
// Recursive cleaning for array [] inputs, not just strings.
$sanitized = static::array_strip_tags(Input::get());
Input::merge($sanitized);
}
public static function array_strip_tags($array)
{
$result = array();
foreach ($array as $key => $value) {
// Don't allow tags on key either, maybe useful for dynamic forms.
$key = strip_tags($key);
// If the value is an array, we will just recurse back into the
// function to keep stripping the tags out of the array,
// otherwise we will set the stripped value.
if (is_array($value)) {
$result[$key] = static::array_strip_tags($value);
} else {
// I am using strip_tags(), you may use htmlentities(),
// also I am doing trim() here, you may remove it, if you wish.
$result[$key] = trim(strip_tags($value));
}
}
return $result;
}
Then put this code in the beginning of your before filter (in application/routes.php):
//Our own method to defend XSS attacks globally.
Common::global_xss_clean();
I just found this question. Another way to do it is to enclose dynamic output in triple brackets like this {{{ $var }}} and blade will escape the string for you. That way you can keep the potentially dangerous characters in case they are important somewhere else in the code and display them as escaped strings.
i'd found this because i was worried about xss in laravel, so this is the packages gvlatko
it is easy:
To Clear Inputs = $cleaned = Xss::clean(Input::get('comment');
To Use in views = $cleaned = Xss::clean(Input::file('profile'), TRUE);
It depends on the user input. If you're generally going to be outputting code they may provide (for example maybe it's a site that provides code snippets), then you'd sanitize on output. It depends on the context. If you're asking for a username, and they're entering HTML tags, your validation should be picking this up and going "no, this is not cool, man!"
If it's like the example I stated earlier (code snippets), then let it through as RAW (but be sure to make sure your database doesn't break), and sanitize on output. When using PHP, you can use htmlentities($string).
I'm working on converting an old define()-based language/translation system to a more flexible one (probably JSON-based, but it's still open).
As part of this conversion, I will need to convert from 42 .php files with several thousand strings each to whatever format I'll be using. Some of the defined strings reference other defines or use PHP code. I don't need to keep this dynamic behaviour (it's never really dynamic anyway), but I will need to have the "current" values at time of conversion.
One define might look like this:
define('LNG_Some_string', 'Page $s of $s of our fine '.LNG_Product_name);
Since all defines have an easily recognizable 'LNG_' prefix, converting a single file is trivial. But I'd like to make a small script which handles all 42 in one run.
Ideally I'd be able to either undefine or redefine the define()'s, but I can't find a simple way of doing that. Is this at all possible?
Alternatively, what would be a good way of handling this conversion? The script will be one-off, so it doesn't need to be maintainable or fast. I just want it fully automated to avoid human error.
if speed is not important, so you can use get_defined_constants function.
$constans = get_defined_constants(true);
$myconst = array();
$myconst = $constans['user'];
$myconst will contain all constants defined by your script:-)
P.S: I'm not a good php coder, it was just a suggestion :-)
You can't undefine constants, but you can generate your new scripts by utiliising them and the constant() function:
<?php
/* presuming all the .php files are in the same directoy */
foreach (glob('/path/*.php') as $file) {
$contents = file_get_contents($file);
$matches = array();
if (!preg_match('/define\(\'LNG_(\w+)\'/', $contents, $matches) {
echo 'No defines found.';
exit;
}
$newContents = '';
include_once $file;
foreach ($matches as $match) {
$newContents .= "SOME OUTPUT USING $match AS NAME AND " . constant($match) . " TO GET VALUE";
}
file_put_contents('new_'.$file, $newContents);
}
?>
Defined constants can't be undefined. They're immutable.
Perhaps what you can do is get in right before they're defined and modify them in certain circumstances.
I'm merging two different versions of a translations array. The more recent version has a lot of changes, over several thousand lines of code.
To do this, I loaded and evaluated the new file (which uses the same structure and key names), then loaded and evaluated the older version, overwriting the new untranslated values in the array with the values we already have translated.
So far so good!
However, I want to be able to echo out the constructor for this new merged array so I can cut and paste it into the new translation file, and have job done (apart from completing the rest of the translations..).
The code looks like this (lots of different keys, not just index):
$lang["index"]["chart1_label1"] = "Subscribed";
$lang["index"]["chart1_label2"] = "Unsubscribed";
And the old..
$lang["index"]["chart1_label1"] = "Subscrito";
$lang["index"]["chart1_label2"] = "Não subscrito";
After loading the two files, I end up with a merged $lang array, which I then want to echo out in the same form, so it can be used by the project.
However, when I do something like this..
foreach ($lang as $key => $value) {
if (is_array($value)) {
foreach ($value as $key2 => $value2) {
echo "$lang['".$key."']"; // ... etc etc
}
}
}
..obviously I get "ArrayIndex" etc etc instead of "$lang". How to echo out $lang without it being evaluated..? Once this is working, can add in the rest of the brackets etc (I realise they are missing), but just want to make this part work first.
If there's a better way to do this, all ears too!
Thanks.
edit:
What I was really looking for was this:
"Note: Unlike the three other syntaxes, variables and escape sequences for special characters will not be expanded when they occur in single quoted strings."
So no need even to escape. May come in handy one day for someone... about merging the arrays, there's a great recursive array merging function on the PHP manual page for array_merge(), which also came in handy.
Thanks to whoever downvoted! Love you too.
Either escape the $: "\$lang" or use single quotes: '$lang'
Note that you really don't need to post a whole page of backstory, if your entire question just boils down to "how do I output a dollar sign in PHP"
there is var_export() function, but I do not understand the purpose of this terrible mess.
It's all too... manual
why not to use a gettext - an industry standard for multi-language site?
or at least some other format, more reliable than plain PHP one?
Programming code is not intended to be written automatically.