Hi I'm using Closure Compiler to compress and join a few JavaScript files the syntax is something like this;
$c = new PhpClosure();
$c->add("JavaScriptA.js")
->add("JavaScriptB.js")
->write();
How could I make it systematically add more files from an array lets say for each array element in $file = array('JavaScriptA.js','JavaScriptB.js','JavaScriptC.js',..) it would execute the following code
$c = new PhpClosure();
$c->add("JavaScriptA.js")
->add("JavaScriptB.js")
->add("JavaScriptC.js")
->add
...
->write();
Thank you so much in advance!
The PhpClosure code uses method chaining to reduce repeated code and make everything look slightly nicer. The functions (or at least the add function) returns $this (The object the function was called on).
The code in the first sample could be written as:
$c = new PhpClosure();
$c->add("JavaScriptA.js");
$c->add("JavaScriptB.js");
$c->write();
The middle section (The add function calls) can then be transformed so that it loops over an array of files rather than having to add a line for each file.
$files = array("JavaScriptA.js", "JavaScriptB.js");
$c = new PhpClosure();
foreach ( $files as $file ){
$c->add($file);
}
$c->write();
Related
Currently I'm trying to clean up as much code as possible of the website of the company I work at, without like completely rewriting (stuff like removing dumb comments like // declare variables, clean variable naming, consistency etc.). The code is a huge mess and terrible, uses lots of files which gets included and variables get used between files which makes it really annoying.
The problem is, a simple thing as renaming a variable actually can break a lot. I currently have the following problem:
In file a I have a query, looking something like this:
$getProductSql = 'QUERY';
$getProduct = $db->prepare($getProductSql);
// some bind values etc.
$getProduct->execute();
$product = $getProduct->fetch(PDO::FETCH_ASSOC);
Now, after that file is included in the index, file b will get included which contains the following:
$getProductsSql = 'QUERY';
$getProducts = $db->prepare($getProductsSql);
// some bind values etc.
$getProducts->execute();
foreach ($getProducts->fetchAll(PDO::FETCH_ASSOC) as $product) {
// some code
}
After that file is included, file c will be included which contains the following:
if ($product['COLUMN'] === '1')
In file c, the $product variable from file a should be used but due to how our structure is and file b getting included in between, $product from file a is replaced with the last value of $product from the loop in file b.
Is there any way to solve this without using 2 different variable names or moving code?
As deceze says: there isn't really ... but you could do this:
Leave file a as is.
Change file b like this:
$getProductsSql = 'QUERY';
$getProducts = $db->prepare($getProductsSql);
// some bind values etc.
$getProducts->execute();
$p[] = $product;
foreach ($getProducts->fetchAll(PDO::FETCH_ASSOC) as $product) {
// some code
$p[] = $product
}
$product = $p;
And file c like this:
if ($product[0]['COLUMN'] === '1')
It is a bit confusing and not really nice. And yes, that is why you should rather use a function ...
Alternatively: If you don't need the product array from file b any longer in file c you could also simply do this:
file b:
$getProductsSql = 'QUERY';
$getProducts = $db->prepare($getProductsSql);
// some bind values etc.
$getProducts->execute();
$p = $product;
foreach ($getProducts->fetchAll(PDO::FETCH_ASSOC) as $product) {
// some code
}
$product = $p;
In that case: no need to change file c
I have a php script getting all folders in a posts folder and making them into a list.
I have a $postinfo_str variable assigned to a json file for each folder which I am using to store post date and category/tag info etc in.
I also have a $pagetitle variable assigned to a title.php include file for each folder. So say I am on a "June 2018" archive page, the text in that file will be "June 2018". If I am on say a "Tutorials" category page, that will be the text in the title.php.
In the json file, I have:
{
"Arraysortdate": "YYYYMMDD",
"Month": "Month YYYY",
"Category": ["cat1", "cat2", "etc"]
}
I am ordering the array newest to oldest using krsort with Arraysortdate as key.
How do I filter the array using $pagetitle as input, finding if there is a match in $postinfo_str, and if there isn't, remove that folder from the array?
All I can seem to find regarding array sorting is where the info in the $pageinfo_str is basically the array and so by that, the $title is the input and the output is the matching text from the $postinfo_str, whereas I want the output to be the folders that only have the matching text in the $postinfo_str to what the input ($pagetitle) is.
Here is my code I have.. Keep in mind this is flat file, I do not want a database to achieve this. See comments if you want an explaination.
<?php
$BASE_PATH = '/path/to/public_html';
// initial array containing the dirs
$dirs = glob($BASE_PATH.'/testblog/*/posts/*', GLOB_ONLYDIR);
// new array with date as key
$dirinfo_arr = [];
foreach ($dirs as $cdir) {
// get current page title from file
$pagetitle = file_get_contents("includes/title.php");
// get date & post info from file
$dirinfo_str = file_get_contents("$cdir/includes/post-info.json");
$dirinfo = json_decode($dirinfo_str, TRUE);
// add current directory to the info array
$dirinfo['dir'] = $cdir;
// add current dir to new array where date is the key
$dirinfo_arr[$dirinfo['Arraysortdate']] = $dirinfo;
}
// now we sort the new array
krsort($dirinfo_arr);
foreach($dirinfo_arr as $key=>$dir) {
$dirpath = $dir['dir'];
$dirpath = str_replace('/path/to/public_html/', '', $dirpath);
?>
<!--HTML HERE SUCH AS--!>
TEXT <br>
<?php
};
?>
I have difficulties following your problem description. Your code example is slightly confusing. It appears to load the same global includes/title.php for each directory. Meaning, the value of $pagetitle should be the same every iteration. If this is intended, you should probably move that line right outside the loop. If the file contains actual php code, you should probably use
$pagetitle = include 'includes/title.php';
or something similar. If it doesn't, you should probably name it title.txt. If it is not one global file, you should probably add the path to the file_get_contents/include as well. (However, why wouldn't you just add the title in the json struct?)
I'm under the assumption that this happened by accident when trying to provide a minimal code example (?) ... In any case, my answer won't be the perfect answer, but it hopefully can be adapted once understood ;o)
If you only want elements in your array, that fulfill certain properties, you have essentially two choices:
don't put those element in (mostly your code)
foreach ($dirs as $cdir) {
// get current page title from file
$pagetitle = file_get_contents("includes/title.php");
// get date & post info from file
$dirinfo_str = file_get_contents("$cdir/includes/post-info.json");
$dirinfo = json_decode($dirinfo_str, TRUE);
// add current directory to the info array
$dirinfo['dir'] = $cdir;
// add current dir to new array where date is the key
// ------------ NEW --------------
$filtercat = 'cat1';
if(!in_array($filtercat, $dirinfo['Category'])) {
continue;
}
// -------------------------------
$dirinfo_arr[$dirinfo['Arraysortdate']] = $dirinfo;
array_filter the array afterwards, by providing a anonymous function
// ----- before cycling through $dirinfo_arr for output
$filtercat = 'cat1';
$filterfunc = function($dirinfo) use ($filtercat) {
return in_array($filtercat, $dirinfo['Category']));
}
$dirinfo_arr = array_filter($dirinfo_arr, $filterfunc);
you should read up about anonymous functions and how you provide local vars to them, to ease the pain. maybe your use case is bettersuited for array_reduce, which is similar, except you can determine the output of your "filter".
$new = array_filter($array, $func), is just a fancy way of writing:
$new = [];
foreach($array as $key => $value) {
if($func($value)) {
$new[$key] = $value;
}
}
update 1
in my code samples, you could replace in_array($filtercat, $dirinfo['Category']) with in_array($pagetitle, $dirinfo) - if you want to match on anything that's in the json-struct (base level) - or with ($pagetitle == $dirinfo['Month']) if you just want to match the month.
update 2
I understand, that you're probably just starting with php or even programming, so the concept of some "huge database" may be frightening. But tbh, the filesystem is - from a certain point of view - a database as well. However, it usually is quite slow in comparison, it also doesn't provide many features.
In the long run, I would strongly suggest using a database. If you don't like the idea of putting your data in "some database server", use sqlite. However, there is a learning curve involved, if you never had to deal with databases before. In the long run it will be time worth spending, because it simplifys so many things.
I am trying to loop through all the php files listed in an array called $articleContents and extract the variables $articleTitle and $heroImage from each.
So far I have the following code:
$articleContents = array("article1.php", "article2.php"); // array of all file names
$articleInfo = [];
$size = count($articleContents);
for ($x = 0; $x <= $size; $x++) {
ob_start();
if (require_once('../articles/'.$articleContents[$x])) {
ob_end_clean();
$entry = array($articleContents[$x],$articleTitle,$heroImage);
array_push($articlesInfo, $entry);
}
The problem is, the php files visited in the loop have html, and I can't keep it from executing. I would like to get variables from each of these files without executing the html inside each one.
Also, the variables $articleTitle and $heroImage also exist at the top of the php file I'm working in, so I need to make sure the script knows I'm calling the variables in the external file and not the current one.
If this is not possible, can you please recommend an alternative method?
Thanks!
Don't do this.
Your PHP scripts should be for your application, not for your data. For your data, if you want to keep it file-based, use a separate file.
There are plenty of formats to choose from. JSON is quite popular. You can use PHP's built-in serialization as well, which has support for more PHP-native types but is not as portable to other frameworks.
A little hacky but seems to works:
$result = eval(
'return (function() {?>' .
file_get_contents('your_article.php') .
'return [\'articleTitle\' => $articleTitle, \'heroImage\' => $heroImage];})();'
);
Where your_article.php is something like:
<?php
$articleTitle = 'hola';
$heroImage = 'como te va';
The values are returned in the $result array.
Explanation:
Build a string of php code where the code in your article scripts are wrapped inside a function that returns an array with the values you want.
function() {
//code of your article.php
return ['articleTitle' => $articleTitle, 'heroImage' => $heroImage];
}
Maybe you must do some adaptations to the strings due <?php ?> tags placements.
Anyway, this stuff is ugly. I'm very sure that it can be refactored in some way.
Your problem (probably) comes down to using parentheses with require. See the example and note here.
Instead, format your code like this
$articlesInfo = []; // watch your spelling here
foreach ($articleContents as $file) {
ob_start();
if (require '../articles/' . $file) { // note, no parentheses around the path
$articlesInfo[] = [
$file,
$articleTitle,
$heroImage
];
}
ob_end_clean();
}
Update: I've tested this and it works just fine.
function KeepSamePage($text)
{
$sb_w = $oPdf->GetStringWidth($text);
$num_lines = explode("\n",$text);
$total = 0;
foreach($num_lines as $line)
{
$y = $oPdf->GetY();
$page_height = 11 * 25.4;
$this_width = $oPdf->GetStringWidth(strip_tags($line));
$extra_line = floor($this_width / $w);
$is_line = $this_width / ($w - 1);
$is_line = $this_width == 0 ? 1 + $extra_line : ceil($is_line) + $extra_line;
$total = $total + $is_line;
}
$sb_height = $total * 5;
if(($page_height - $y) < $sb_height){ $oPdf->AddPage(); }
}
KeepSamePage($signature_block);
I'm using FPDF and I'm creating a function to keep the signature page of a letter all on the same page. This checks to see if it would go to the next page and if soo, then it does an AddPage();
The issue I'm having is that when I don't have it in a function, it works perfectly, but when I put it within a function, I get errors when calling the methods in the class represented by $oPdf.
So, my question generally is this: Is it possible to have a regular function in PHP call a class method as I have below? If it is possible, what am I doing wrong?
ERROR GENERATED IS:
Fatal error: Call to a member function GetStringWidth() on a non-object in /home/jarodmo/public_html/cms/attorney_signature_block.php on line 18
Oh, and an explanation of my function just in case you're interested or someone else finds it.
Text has \n for new lines in it so the PDF will put the text of the signature block on the next line. Each new array element should be a new line, so I would need to multiply the number of lines by my line height, 5 in this case. (See $total * 5).
I check to see where we are on the page, find the difference between the page height and the Y position, then check that against the height of the signature block. If the signature block is bigger, then it wouldn't fit and I know we need a manual page break.
Also, because I do the explode with the \n to see the lines, I also have to check to make sure that none of the lines is still wider than the page otherwise it would word wrap and really be 2 lines (or more) where I was only counting it as 1 because it was just one array element. I know a signature block shouldn't have text wide enough to be on 2 lines, but I wrote this to be applicable for more than just signature blocks. I wanted to be able to call this function anywhere I wanted to make sure certain text stayed on the same page. Call the function, check the text I'm about to write to the PDF and move on knowing that the desired text would all be on the same page.
Thanks for all of the help and comments. SO is the best.
$oPdf
is not defined on your code. You need to define it, and maybe read PHP variable scope.
You are trying to access methods of the $oPdf object in your function, but your function has no idea what $oPdf is, thus, the error message.
you have to do something like this.
function KeepSamePage($text) {
$oPdf = new your_string_class();
$sb_w = $oPdf->GetStringWidth($text);
}
or
$oPdf = new your_string_class();
function KeepSamePage($text, $oPdf) {
$sb_w = $oPdf->GetStringWidth($text);
}
Try the following:
function KeepSamePage($text) {
global $oPdf;
…
}
The problem is, that the object is defined outside your function and you will have to allow your function to access it.
// Edit:
If you want to avoid global for whatever reason, you will have to pass your object to the function like this:
function KeepSamePage($text, $oPdf) {
…
// IMPORTANT! $oPdf has changed in this function, so you will have to give it back
return $oPdf;
}
You can call your function like this:
$oPdf = KeepSamePage($signature_block, $oPdf);
The advantage is, that you see in the main thread, that your function might has changed the object.
// Edit 2: I think, I was wrong on the edit1 in your case. As you pass the complete object to the function every change does apply to the object, so the changes will still be existant without giving back the result. If this was a variable that was defined in the main thread, you would have to give back the new value:
$a = 1;
function result1($a) {
++$a;
}
function result2($a) {
return ++$a;
}
echo $a."\n"; // 1
result1($a);
echo $a."\n"; // 1
$a = result2($a);
echo $a."\n"; // 2
I have an XML document that I am trying to get some of the values for and don't know how to get to the attributes. An example of the structure and values are below:
<vin_number value="3N1AB51D84L729887">
<common_data>
<engines>
</engines>
</common_data>
<available_vehicle_styles>
<vehicle_style name="SE-R 4dr Sedan" style_id="100285116" complete="Y">
<engines>
<engine brand="" name="ED 2L NA I 4 double overhead cam (DOHC) 16V"></engine>
</engines>
</vehicle_style>
</available_vehicle_styles>
</vin_number>
I am trying to get the engine["name"] attribute (NOT "ENGINES"). I thought the following would work but I get errors (I cant parse past "vehicle_style")
$xml = simplexml_load_file($fileVIN);
foreach($xml->vin_number->available_vehicle_styles->vehicle_style->engines->engine->attributes() as $a => $b) {
echo $b;
}
Assuming your XML is structured in the same was as this example XML, the following two snippets will get the engine name.
The property hierarchy way (split onto multiple lines so you can read it).
$name = (string) $xml->vin_number
->available_vehicle_styles
->vehicle_style
->engines
->engine['name'];
Or the more concise XPath way.
$engines = $xml->xpath('//engines/engine');
$name = (string) $engines[0]['name'];
Unless there are multiple engine names in your XML, there is no need to use a foreach loop at all.
(See both snippets running on a codepad.)
Use the SimpleXMLElement::attributes method to get the attributes:
foreach($xml->available_vehicle_styles->vehicle_style as $b) {
$attrs = $b->attributes();
echo "Name = $attrs->name";
}
Note: I slightly changed the "path" to the element starting from $xml because that's how it loaded the fragment for me.
By this layout, there could be more than one engine per engines block, so you have to explicitly pick the first one. (Assuming you know for sure there's only going to be one.)
$name = $xml->available_vehicle_styles->vehicle_style->engines->engine[0]->attributes()->name;