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);
Related
Update 2:
Regarding the error "Call to a member function children() on null", I found these functions.
public function hasChildren()
{
return $this->hasChildNodes();
}
public function children($query = null)
{
$children = array();
if (!$this->hasChildren()) {
return $children;
}
if ($query == null) {
foreach ($this->childNodes as $child) {
if ($child->nodeType == XML_ELEMENT_NODE) {
$children[] = $child;
}
}
return $children;
}
return $this->query(CssSelector::toXPath($query, 'child::'));
}
public function removeChildren()
{
while ($child = $this->firstChild) {
$this->removeChild($child);
}
return $this;
}
#########################################
Update:
I tried to change the code to
public function before(...$data): void
{
foreach($data as $item) {
$item = $this->prepareInsert($item);
$this->parentNode->insertBefore($item, $this);
}
}
This seems to work, but I get more of those errors. At one point I changed the code from:
public function prepend($data)
{
$data = $this->prepareInsert($data);
if (isset($data)) {
if ($this->hasChildren()) {
$this->insertBefore($data, $this->firstChild);
} else {
$this->appendChild($data);
}
}
return $this;
}
to this:
public function prepend(...$data): void
{
foreach($data as $item)
{
$item = $this->prepareInsert($item);
if (isset($item)) {
if ($this->hasChildren()) {
$this->insertBefore($item, $this->firstChild);
} else {
$this->appendChild($item);
}
}
}
}
Now, I get on my page the error message:
Call to a member function children() on null
There are no other information.
we have installed Joomla 3.10.10 and use the template "Effortless" from BDThemes (which we bought a long time ago via Envato). The template is based on the Warp 7 framework. However, our version is outdated and can no longer be updated, as the template is no longer available on Envato. Currently we are still using PHP 7.4, when we upgrade to PHP 8.0 we get the error message:
"Fatal error: Declaration of Warp\Dom\Element::before($data): void must be compatible with DOMElement::before(...$nodes): void in /homepages/13/d238860405/htdocs/homecms_ta/templates/effortless/warp/src/Warp/Dom/Element.php on line 108"
The code:
public function before($data)
{
$data = $this->prepareInsert($data);
$this->parentNode->insertBefore($data, $this);
return $this;
}
Unfortunately, I don't know how to fix it. I would be very grateful for any help.
There are some newer methods of Element (not available during the age of IE), for example:
https://developer.mozilla.org/en-US/docs/Web/API/Element/after
https://developer.mozilla.org/en-US/docs/Web/API/Element/before
https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend
https://developer.mozilla.org/en-US/docs/Web/API/Element/append
Apparently newer versions of DOMElement PHP tried to follow it too.
All of those are variadic, but the old code of the theme may have assumed that you only had one element to insert.
The easiest way to go around it (minimal change) is probably to use a variadic argument ...$your_variable_name as $data, then use foreach($your_variable_name as $data){...} around your data. And we have to see if there are other side effects (if someone ever uses $element -> before($oneThing) -> before($anotherThing), the second one would fail (as the return type of the first before is void, unlike the old return of $this where you can use ->before directly after that).
The return $this probably won't get used (I'm not sure about that, though).
For example:
before
public function before(...$nodes): void
{
foreach($nodes as $data){
$data = $this->prepareInsert($data);
$this->parentNode->insertBefore($data, $this);
}
// return $this;
}
prepend
public function prepend(...$nodes): void
{
foreach($nodes as $data){
$data = $this->prepareInsert($data);
if (isset($data)) {
if ($this->hasChildren()) {
$this->insertBefore($data, $this->firstChild);
} else {
$this->appendChild($data);
}
}
}
// return $this;
}
(note that I can't test it myself as I don't have the complete code of the template you used)
Is there any way to use a variable of a method, after a recursive call (without sending that as a parameter).
For example:
class Doc {
public function readDoc($file, $recursion = false) {
if ($recursion != false) {
// DO SOMETHING;
}
$filename = $file."Some added text";
$this->readDoc($filename, 1);
}
}
Here, is it possible to use the value of $file sent in the first call (when the readDoc() function is called recursively).
You can create a simple stack with an array, e.g.
class Doc {
private $stack = [];
public function readDoc($file, $recursion=false) {
if($recursion != false)
DO SOMETHING
$this->stack[] = $file;
$filename = $file."Some added text";
$this->readDoc($filename, 1);
}
}
And then get the first index of the array as your $file variable.
you could also use an anonymous function to work in a different scope something like this:
public function test($file, $recursion = false)
{
$rec = function($r) use ($file, &$rec)
{
if($r !== false) {
}
$filename = $file.'test';
return $rec($r);
};
return $rec($recursion);
}
in this case the $file variable always stays the same
(be aware that the above example creates an infinite-loop)
I searched around on stackoverflow about the "Using $this when not in object context" but I could't get my answer out of it so I ask it here.
I will just copy my code here so you and me dont get confused:
the error is in this code on the first IF statement($this->checkConnection()):
public function randomLocation() {
if ($this->checkConnection()) {
$fetch = array();
$character = $this->getCharacter($_GET['action']);
$locations = Controller_Core_Database::getObjects('Model_Game_Location');
foreach ($locations as $location) {
$fetchLocation = $location->getLocationNameShort();
$fetch[] = $fetchLocation;
}
$newLocation = rand(0,31);
$character->setLocation($fetch[$newLocation]);
Controller_Core_Database::update($character);
}
}
But I have many functions that begins with this 'checkConnection' and those work, except for this one. which is strange in my opinion(although i'm still a beginner with OOP PHP)
so this is my checkConnection function:
public function checkConnection() {
$mysqli = Controller_Core_Database::getDB();
if ($mysqli != null) {
return TRUE;
} else {
return FALSE;
}
}
the Controller_Core_Database::getDB() code is:
public static function getDB() {
self::$mysqli = new mysqli('','','','');
return self::$mysqli;
}
I have remove the information for security purposes.
And here is a example of a function that works perfectly:
public function createItem() {
$view = new Controller_Core_View();
$view->setViewFile("game/createItem.php");
if ($this->checkConnection()) {
$item = Controller_Core_Database::getObjects('Model_Game_Item');
$enchantment = Controller_Core_Database::getObjects('Model_Game_Itemenchantment');
$view->assign("item", $item);
$view->assign("enchantment", $enchantment);
}
return $view->render();
}
I dont see any different between the 2 functions that works and not works, I hope you can help me
You didn't specified, how randomLocation() method is called.
I supose you are calling that non-static method staticaly - thats why you are not in an object context.
If so, consider rewriting either call to randomLocation() method to use object context - e.i.
$x = new YourObjectName();
$x->randomLocation();
or rewrite checkConnection() method to be static and call it staticaly in randomLocation() method.
To avoid getting the error message as in this previous question, I decided to change the class with __get() like this below,
class property
{
public function __get($name)
{
return isset($this->$name) ? $this->$name : new property;
}
}
class objectify
{
public function array_to_object($array = array(), $property_overloading = false)
{
# if $array is not an array, let's make it array with one value of former $array.
if (!is_array($array)) $array = array($array);
# Use property overloading to handle inaccessible properties, if overloading is set to be true.
# Else use std object.
if($property_overloading === true) $object = new property();
else $object = new stdClass();
foreach($array as $key => $value)
{
$key = (string) $key ;
$object->$key = is_array($value) ? self::array_to_object($value, $property_overloading) : $value;
}
return $object;
}
}
$object = new objectify();
$type = null;
$type = $object->array_to_object($type,true);
var_dump($type->a->b->c);
so I get this result in the end,
object(property)#3 (0) { }
but it is still not perfect. as my understanding, the above solution processes the object in a chain like this,
$type = object{}->object{}->object{}
so I wonder if I can find whether it is the last chain and it is empty then just output a null?
$type = object{}->object{}->NULL
is it possible with PHP?
EDIT:
I have thought of an idea which is to count how many times the property class has been instantiated,
class property
{
public static $counter = 0;
function __construct() {
self::$counter++;
}
public function __get($name)
{
if(isset($this->$name))
{
return $this->$name;
}
elseif(property::$counter < 3)
{
return new property;
}
else
{
return null;
}
}
}
but my only problem is how to make the number 3 dynamic. Any ideas?
Sounds like you're looking for a PHP version of Groovy's ?. operator: http://groovy.codehaus.org/Null+Object+Pattern
Afaik, you can't overload or create a new operator in PHP. You could perhaps simulate it by passing all of your nested calls to a function, and the function knows when to return null.
Edit: other options posted here - http://justafewlines.com/2009/10/groovys-operator-in-php-sort-of/
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