I'm making a script that gets multiple CSS files from a webpage and put them together using foreach() and include().
I already found the right function:
function GetBetween($content, $start, $end) {
$r = explode($start, $content);
if(isset($r[1])) {
$r = explode($end, $r[1]);
return $r[0];
}
return '';
}
Is there an alternative to this function to get all in an array?
How can I use this to get multiple strings in an array?
For example:
foreach($css = GetBetween($page, '<link rel="stylesheet" href="','"') {
include("$css");
}
Actually by returning an array from the function you could iterate over it. Example:
$cssFiles = GetBetween($page, '<link rel="stylesheet" href="','"');
foreach ($cssFiles as $cssFile)
{
include($cssFile);
}
However your GetBetween function does not return an array so far. Also you're trying to parse HTML here, you don't need to write a function to parse it, but instead you can make use of the existing HTML Parser in PHP:
/**
* All stylesheet files within a HTML source
*/
class StyleSheets extends DOMDocument implements IteratorAggregate
{
public function __construct($source)
{
parent::__construct();
$this->loadHTML($source);
}
public function getIterator()
{
static $array;
if (NULL === $array)
{
$xp = new DOMXPath($this);
$expression = '//head/link[#rel="stylesheet"]/#href';
$array = array();
foreach($xp->query($expression) as $node)
$array[] = $node->nodeValue;
}
return new ArrayIterator($array);
}
}
You can then easily use it:
foreach(new StyleSheets($page) as $index => $file)
{
printf("#%d: %s\n", $index, $file);
}
Demo / Example output:
#0: file1.css
#1: file2.css
#2: file3.css
Technically you could do something similar with a string search as you do with your GetBetween function making it return an array as well. However as it's HTML I suggest you use a HTML parser instead so it's more fail-safe.
Related
I got this function from a question here. when I try on a separate file it runs normally. but when I rewrite it in a class that contains more functions I can rather call it in another file, the searchRec contained in this function (calling the function itself) turns red or is marked as an error by visual studio code. whereas before, above this function I also wrote the same function in which there is a function call itself, and it runs normally.
public function searchRec($haystack, $needle, $pathId=Array(), $pathIndex=Array())
{
foreach($haystack as $index => $item) {
$pathId[] = $item['Id'];
$pathIndex[] = $index;
if($item['Title'] == $needle) {
$returnObject = new stdClass();
$returnObject->match = $item;
$returnObject->pathId = $pathId;
item directly
$returnObject->pathIndex = $pathIndex;
return $returnObject;
}
if(isset($item['Children']) && count($item['Children']>0)) {
(recursively)
$result = searchRec($item['Children'], $needle, $pathId, $pathIndex); //searchRec error, VCS say: undifined function
if($result) {
return $result;
}
}
}
return false;
}
Since it's a class method, you need to call it with object-oriented syntax.
$result = $this->searchRec($item['Children'], $needle, $pathId, $pathIndex);
I am building a PHP function to enqueue JavaScript files into a PHP array and then have another PHP function that will load all the JS files into a page and load them in the order based on a sort number that can be passed into the enqueue function. Similar to how WordPress loads JS and CSS files.
So my PHP function enqueue_js_script() might look like this below which takes in a key name for the JS file, a file path to the JS file, and a sort order number which is optional. It then would add the JS file to a PHP class property $this->_js_files[$script_key]...
public function enqueue_js_script($script_key, $file_source, $load_order = 0){
$this->_js_scripts[$script_key] = $file_source;
}
Then I will also have a PHP function load_js_scripts() which will print each script file path into the header of a webpages HTML.
This is where I want to take into consideration the $load_order passed into enqueue_js_script() to print the scripts into the HTML in the order based on these numbers.
How can I use this sort order number to sort my array of JS scripts?
UPDATE
It looks like I should store the sort number in an array like this instead...
$this->_js_files[$script_key] = array(
'file_source' => $file_source,
'sort' => $load_order
);
Using usort and a custom sorting function:
<?php
public function enqueue_js_script($script_key, $file_source, $load_order = 0){
$jsScript = new \stdClass;
$jsScript->load_order = $load_order;
$jsScript->script_key = $script_key;
$this->_js_scripts[$script_key] = $jsScript;
}
function sortJSFiles($a, $b)
{
if ($a->load_order == $b->load_order) {
return 0;
}
return ($a->load_order < $b->load_order) ? -1 : 1;
}
usort($this->_js_scripts, "sortJSFiles");
Having to pass your array key is not really good practice. The $array[] = $foo construction adds $foo as the new last item of $array.
Using usort.
<?php
class OrderStack {
private $contents = array();
public function add($order, $load) {
if (!is_int($order) || $order < 0) {
throw new InvalidArgumentException("$order must be a non-negative integer");
}
$this->contents[] = array($order, $load);
}
public function get_array() {
usort(
$this->contents,
'OrderStack::compare'
);
return array_map(
'OrderStack::get_load',
$this->contents
);
}
private static function get_load($stack_item) {
return $stack_item[1];
}
private static function compare($a, $b) {
return $a[0] - $b[0];
}
}
class YourClass {
private $_js_scripts;
public function __construct() {
$this->_js_scripts = new OrderStack();
}
public function enqueue_js_script($file_source, $load_order = 0) {
$this->_js_scripts->add($load_order, $file_source);
}
public function get_js_scripts() {
return $this->_js_scripts->get_array();
}
}
?>
The OrderStack class is reusable.
How to refactor this snippet of code, to reduce indentation level by one?
I just wonder is it possible in PHP to write this code in a diffrent way, with just one level of indentation.
The code:
private function isArrayMatchingCriteria(array $array) {
foreach($array as $element) {
if (! $this->isElementMatchingCriteria($element) {
return false;
}
}
return true;
}
Please take into consideration, that:
this code doesn't always iterate over all array elements - so combination of count + array_filter / array_map isn't the same
it is easy to do by introducing a dedicated object attribute serving as a flag, but I'm looking for a way without introducing new attributes
If you're just looking to remove indentation, you could use:
private function isArrayMatchingCriteria(array $array) {
foreach($array as $element) {
if (!$this->isElementMatchingCriteria($element)) return false;
}
return true;
}
Use array_map, something like this:
class MyClass
{
private function isElementMatchingCriteria( $element )
{
// DUMMY, replace with actual code
if ( $element == "foo" || $element == "bar" ) {
return true;
} else {
return false;
}
} // end is Element Matching Criteria
public function isArrayMatchingCriteria(array $array)
{
$results = array_map( array( $this, "isElementMatchingCriteria"), $array );
$isMatch = true;
foreach ( $results as $result ) {
$isMatch = $isMatch && $result;
} // end foreach
return $isMatch;
} // end function isArrayMatchingCriteria
} // end MyClass
$myClass = new MyClass();
$array = array( "foo", "bar", "baz" );
$result = $myClass->isArrayMatchingCriteria( $array );
print_r( $result );
What is the best, most concise coding practice for eliminating the final comma when generating a comma-delimted list in a typical for loop? This comes up ALL THE TIME and I cannot stand writing so many extra lines of code for something so simple... there must be a better technique/pattern.
foreach ($list as $item)
{
echo "'".$item . "',";
}
What is the best way (using PHP and/or JS) to make the above code produce a comma everywhere but the last iteration?
Right now I am doing something like this:
$total = count($images);
$i=0;
foreach ($list as $item)
{
$i++;
echo "'".$item."'";
if ($i<$total) echo ',';
}
But this adds FOUR LINES OF CODE for something so simple...
The Standard PHP Library (SPL) offers a handy class, CachingIterator that can be used to see if there are any more items to be iterated over. The following may not be as concise as you would like but it is flexible (i.e. can be used for far more than just arrays).
$array = range('a', 'g');
$cache = new CachingIterator(new ArrayIterator($array));
foreach ($cache as $item) {
echo "'$item'";
if ($cache->hasNext()) {
echo ',';
}
}
The above example outputs
'a','b','c','d','e','f','g'
In case you didn't simplified the code example:
echo implode(',', $list);
does it (see also implode PHP Manual ).
If there is more code within the foreach loop you need to keep track whether or not you're on the last element and deal with the situation:
$count = count($list);
$current = 0;
foreach ($list as $item)
{
$current++;
$notLast = $current !== $count;
echo $item;
if ($notLast) echo ',';
}
Edit: you added a similar code to the question after I answered this, so if that is too burdensome for your coding fingers and especially (and understandable) you don't want to repeat such code all day long the same, you need to encapsulate it for re-use. One solution is to implement this within a re-useable iterator:
$list = array('a', 'b', 'c');
class PositionKnowingIterator implements iterator
{
/**
* #var iterator
*/
private $inner;
private $count;
private $index;
public function __construct(array $list) {
// NOTE: implement more iterators / objects to deal with in here
// if you like. This constructor limits it to arrays but
// more is possible.
$this->count = count($list);
$this->inner = new ArrayIterator($list);
}
/* SPL iterator implementation */
public function current() {
return $this->inner->current();
}
public function next() {
$this->index++;
$this->inner->next();
}
public function key() {
$this->inner->key();
}
public function rewind() {
$this->index = 1;
$this->inner->rewind();
}
public function valid() {
return $this->inner->valid();
}
/* Position Knowing */
public function isLast() {
return $this->index === $this->count;
}
public function notLast() {
return !$this->isLast();
}
public function isFirst() {
return $this->index === 1;
}
public function notFirst() {
return !$this->isFirst();
}
public function isInside() {
return $this->notFirst() && $this->notLast();
}
}
foreach($iterator = new PositionKnowingIterator($list) as $item)
{
echo "'".$item."'", $iterator->notLast() ? ',' : '';
}
echo implode(",", $list);
without using foreach needed
User implode() function to achieve this. Sometimes it's also necessary to put something around, for example, to quote SQL fields' values:
$fields = '"' . join('", "', $values) . '"';
And for JavaScript use Array.join() method (W3C):
var implodedString = someArray.join(',')
if I get you right, you can use the implode function.
This does the job for you.
BR,
TJ
Why not:
echo implode(",", $list);
?
The common used practice: using 'join' function or its analog. This function exists almost in every language, so it's most simple, clear and environment independent approach.
echo join(", ", $list);
In Python (and others), you can incrementally process large volumes of data by using the 'yield' operator in a function. What would be the similar way to do so in PHP?
For example, lets say in Python, if I wanted to read a potentially very large file, I could work on each line one at a time like so (this example is contrived, as it is basically the same thing as 'for line in file_obj'):
def file_lines(fname):
f = open(fname)
for line in f:
yield line
f.close()
for line in file_lines('somefile'):
#process the line
What I'm doing right now (in PHP) is I'm using a private instance variable to keep track of state, and acting accordingly each time the function is called, but it seems like there must be a better way.
There is a rfc at https://wiki.php.net/rfc/generators adressing just that, which might be included in PHP 5.5.
In the mean time, check out this proof-of-concept of a poor mans "generator function" implemented in userland.
namespace Functional;
error_reporting(E_ALL|E_STRICT);
const BEFORE = 1;
const NEXT = 2;
const AFTER = 3;
const FORWARD = 4;
const YIELD = 5;
class Generator implements \Iterator {
private $funcs;
private $args;
private $key;
private $result;
public function __construct(array $funcs, array $args) {
$this->funcs = $funcs;
$this->args = $args;
}
public function rewind() {
$this->key = -1;
$this->result = call_user_func_array($this->funcs[BEFORE],
$this->args);
$this->next();
}
public function valid() {
return $this->result[YIELD] !== false;
}
public function current() {
return $this->result[YIELD];
}
public function key() {
return $this->key;
}
public function next() {
$this->result = call_user_func($this->funcs[NEXT],
$this->result[FORWARD]);
if ($this->result[YIELD] === false) {
call_user_func($this->funcs[AFTER], $this->result[FORWARD]);
}
++$this->key;
}
}
function generator($funcs, $args) {
return new Generator($funcs, $args);
}
/**
* A generator function that lazily yields each line in a file.
*/
function get_lines_from_file($file_name) {
$funcs = array(
BEFORE => function($file_name) { return array(FORWARD => fopen($file_name, 'r')); },
NEXT => function($fh) { return array(FORWARD => $fh, YIELD => fgets($fh)); },
AFTER => function($fh) { fclose($fh); },
);
return generator($funcs, array($file_name));
}
// Output content of this file with padded linenumbers.
foreach (get_lines_from_file(__FILE__) as $k => $v) {
echo str_pad($k, 8), $v;
}
echo "\n";
PHP has a direct equivalent called generators.
Old (pre php 5.5 answer):
Unfortunately, there isn't a language equivalent. The easiest way is to either to what you're already doing, or to create a object that uses instance variables to maintain state.
There is however a good option if you want to use the function in conjunction with the foreach-statement: SPL Iterators. They can be used to achieve something quite similar to python generators.
I prototype everything in Python before implementing in any other languages, including PHP. I ended up using callbacks to achieve what I would with the yield.
function doSomething($callback)
{
foreach ($something as $someOtherThing) {
// do some computations that generates $data
call_user_func($callback, $data);
}
}
function myCallback($input)
{
// save $input to DB
// log
// send through a webservice
// etc.
var_dump($input);
}
doSomething('myCallback');
This way each $data is passed to the callback function and you can do what you want.
Extending #Luiz's answer - another cool way is to use anonymous functions:
function iterator($n, $cb)
{
for($i=0; $i<$n; $i++) {
call_user_func($cb, $i);
}
}
$sum = 0;
iterator(10,
function($i) use (&$sum)
{
$sum += $i;
}
);
print $sum;
There may not be an equivalent operator, but the following code is equivalent in function and overhead:
function file_lines($file) {
static $fhandle;
if ( is_null($fhandle) ) {
$fhandle = fopen($file, 'r');
if ( $fhandle === false ) {
return false;
}
}
if ( ($line = fgets($fhandle))!== false ) {
return $line;
}
fclose($fhandle);
$fhandle = null;
}
while ( $line = file_lines('some_file') ) {
// ...
}
That looks about right. Sorry, I haven't tested it.
The same sentence 'yield' exists now on PHP 5.5:
http://php.net/manual/en/language.generators.syntax.php