Converting xml DOMDocument to HTML string and echo - php

I have this Class
Class Parser extends DOMDocument {
public function setHTML($file) {
$this->loadHTMLFile($file);
}
public function setAttribute($name, $value) {
return $this->setAttribute($name, $value);
}
public function findById($id) {
return $this->getElementById($id);
}
}
and I use it like this:
$parser = new Parser();
$parser->setHTML('as.html');
$parser->findById("xaxa")->setAttribute('name1', 'value1');
but if I have to see the changed HTML I call SAVEHTML like this
echo $parser->saveHTML();
Is there a way to make it automatic? Like when method setAttribute is called to make
$this->saveHTML()
auto so I will have this
$html =$parser->findById("xaxa")->setAttribute('name1', 'value1');
then call
echo $html;
Thanks alot

Well, the DOM object cannot be used (directly) as string, and when you'll try for example echo it will throw an exception
Catchable fatal error: Object of class DOMDocument could not be converted to string in ...
The saveHTML method is explicitly designed to return the nodes as HTML string - than you can echo it. Bear in mind, that actually the nodes are already updated (saved!) after you call the setAttribute method - the saveHTML is just for rendering a html string from the nodes.
Providing I understand your concept, and you still think you want it your way, maybe you can try the below solution - but just for the record, I didnt test the code.
Class Parser extends DOMDocument
{
public function setHTML($file)
{
$this->loadHTMLFile($file);
}
public function setAttribute($name, $value)
{
return $this->setAttribute($name, $value);
}
public function findById($id)
{
return $this->getElementById($id);
}
public function __toString()
{
return $this->saveHTML();
}
}
// and now this should work
$parser = new Parser();
$parser->setHTML('as.html');
$parser->findById("xaxa")->setAttribute('name1', 'value1');
echo $parser;

Related

Objects field in callback function PHP

I'm creating a class to manage user submitted articles. Within this class, I'm also using the PHP Simple Dom Parser class. This class allows you to apply a callback function when it outputs HTML, which I use to filter out unwanted elements. Outside of a class, in procedural style, it's implemented by doing this:
<?php
$html = file_get_html($_FILES["repfile"]["tmp_name"]);
function fileFilter($element){
$toDelete = array("img", "script", "object", "iframe");
foreach($toDelete as $el){
if($element->tag==$el){
$element->outertext = "";
}
}
}
$html->set_callback("fileFilter");
$finalContent = (string)$html->find("div", 0)->innertext;
?>
Now what I want to do in my class is something along the lines of
<?php
class Article{
public $blockedElements;
public $html;
function __toString(){
$html->set_callback("Article::htmlFilter");
return (string)$html;
}
public static function htmlFilter($element){
foreach($this->blockedElements as $el){
if($element->tag==$el){
$element->outertext = "";
}
}
}
}
?>
The obvious problem is that you can't use the $this->blockedElements in a static method, so how would I be able to implement this?
By not making htmlFilter static.
Assuming you got an Article instance, you can pass the instance as a callback as well:
function __toString(){
$html->set_callback(array($this, "htmlFilter"));
return (string)$html;
}
If you do that, then htmlFilter will also be called as an instance method (provided you remove the static keyword).

Referencing to DOMDocument method from DOMElement

I have written below classes to make certain DOM operations easier. I want the Easy_Dom_Element's functions to be able to accept both a string and an element as input though. To do that I have to access DOMDocument's createElement method. The call to Easy_Dom::toElement works fine, but $this within that method points to the Easy_Dom_Element instead of Easy_Dom itself. I've tried a static call to createElement like so: Easy_Dom::createElement($element) but for some reason that is not allowed.
class Easy_Dom extends DOMDocument{
/*function __construct(){
$this->registerNodeClass('DOMElement', 'Easy_Dom_Element');
}*/
//Gets the first element by tag name
function getElement($tagName){
return $this->getElementsByTagName($tagName)->item(0);
}
//Creates DOMElement from string if needed
function toElement($element){
if(is_string($element))$element = $this->createElement($element);
return $element;
}
}
class Easy_Dom_Element extends DOMElement{
function prependChildEl($element){
$element = Easy_Dom::toElement($element);
$this->insertBefore($element, $this->firstChild);
return $element;
}
function appendChildEl($element){
$element = Easy_Dom::toElement($element);
$this->appendChild($element);
return $element;
}
}
$_testxml = new Easy_Dom('1.0', 'ISO-8859-1');
$_testxml->registerNodeClass('DOMElement', 'Easy_Dom_Element');
//load defaults
$_testxml->load('default.xml');
//test above classes
$test = $_testxml->getElement('general_title');
$test->appendChildEl('test');
echo $test->nodeValue;
echo $_testxml->saveXML();
Just when I was about to give up on this I finally figured it out, it turns out the answer was really simple.
Just reference the DOMElement's DOMDocument using the ownerDocument property like this:
$DOMDocumentFunctionResult = $this->ownerDocument->DOMDocumentFunction();
So in my example:
class Easy_Dom extends DOMDocument{
/*function __construct(){
$this->registerNodeClass('DOMElement', 'Easy_Dom_Element');
}*/
//Gets the first element by tag name
function getElement($tagName){
return $this->getElementsByTagName($tagName)->item(0);
}
//Creates DOMElement from string if needed
function toElement($element){
if(is_string($element))$element = $this->createElement($element);
return $element;
}
}
class Easy_Dom_Element extends DOMElement{
function prependChildEl($element){
$element = $this->ownerDocument->toElement($element);
$this->insertBefore($element, $this->firstChild);
return $element;
}
function appendChildEl($element){
$element = $this->ownerDocument->toElement($element);
$this->appendChild($element);
return $element;
}
}
What version of PHP are you using?
< PHP 5.3 doesn't allow calling of static methods of inherited classes.
See : http://uk3.php.net/lsb

Convert static method to lambda in PHP

I want to get static method from class and copy it to variable.
This is non-working example illustrating my question:
class foo
{
public static function bar($argument){ return 2*$argument; }
}
$class = new ReflectionClass('foo');
// here is no ReflectionMethod::getClosure() method in reality
$lambda = $class->getMethod('bar')->getClosure();
echo $lambda(3);
So my question: is this possible by any normal way? I find only one way for now. I can parse source file, get method source from it and convert it using create_function() but it's too perverse.
Just wrap it with closure.
$lamda = function($argument){return foo::bar($argument);};
Or you can try to use something like this
function staticMethodToClosure($class, $method) {
return function($argument)use($class, $method){return $class::$method($argument);};
}
An array in the format array($className, $methodName) is invokable as a static method call so this may work for you.
class foo
{
public static function bar($argument){ return 2*$argument; }
public static function getStaticFunction($arg){
return array("foo", $arg);
}
}
$a = foo::getStaticFunction("bar");
echo $a(5); // echos 10

can I pass __construct parameters after a class has been instantiated into an object?

I have a similar code snippet like this
class Search
{
public function search($for, $regEx, $flag) //I would like this to be the constructor
{
// logic here
return $this;
}
}
Then I have another class that creates an object from it, later than tries to use the object.
class MyClass
{
public function start()
{
$this->search = new Search();
}
public function load()
{
$this->search($for, $regEx, $flag);
}
}
My question is, is it possible to create an object first THEN give it the parameters?
I know there are some way around this BUT I only ask because I want to use the object like this
$this->search($params);
// I have my methods chained, so I could use it in one line like
// $this->search($params)->hasResults();
if ($this->search->hasResults()) {
echo 'found stuff';
} else {
echo 'didn't find anything';
}
The way I have it set up right now, I would need to use it like this
$this->search->search($params);
if ($this->search->hasResults()) {
echo 'found stuff';
} else {
echo 'didn't find anything';
}
I have a method called search() that does the logic, and I don't want to be redundant in my naming nor do I want to change the name of the method.
I know another way to keep the visual appeal sane I could pass a variable like so
$search = $this->search->search($params);
then
$search->hasResults();
At the same time I am trying to introduce myself to new OOP concepts and learn from them. Would this require passing things by reference? or setting up some type of magic method?
While the previous anwsers show that you can, I wouldn't use it, because it breaks the concept of encapsulation. A proper way to achieve what you want is the following
class Search
{
public function __constructor($for='', $regEx='', $flag='')
{
$this->Setup($for, $regEx, $flag);
}
public function Setup($for, $regEx, $flag)
{
//assign params
//clear last result search
//chain
return $this;
}
public function search()
{
// logic here
return $this;
}
}
In this way, you can reuse the object and have the params in the constructor, without breaking encapsulation.
Yes it is possible
See the below example
<?php
class a{
public $a = 5;
public function __construct($var){
$this->a = $var;
}
}
$delta = new a(10);
echo $delta->a."\n";
$delta->__construct(15);
echo $delta->a."\n";
Output will be:
10 15
Yep, you can.
class Example {
public $any;
function __counstruct($parameters,$some_text) {
$this->any=$some_text;
return $this->any;
}
}
You can call constructor:
$obj = new Example (true,'hello');
echo $obj->any;
$obj->__construct(true,'bye-bye');
echo $obj->any;
I was able to create the visual coding I wanted by using the __call() magic method like this
public function __call($name, $params)
{
$call = ucfirst($name);
$this->$name = new $call($params);
}
from there I could use this
$this->test->search($params);
$this->test->search->hasResults();
I of course now set the search() method to the class constructor

PHP Chaining... I just can't get it!

I'm trying to create a chaining function for working with strings that are returned from an XML file.
1 original string may have multiple replacements, some of which come from the XML file.
Here is the ugly and standard wrapped approach:
str_replace("what","is meant", str_replace("name","randomer",str_replace("blah", "hello", $string1)));
Here is the approach I'm trying to replicate (like Java):
$string1.replace("blah","hello").replace("name","randomer").replace("what","is meant");
With the above, it works easily... until I use the XML function to get the replacing string.
Here's my class:
class resources{
private static $instance, $string;
public static function getString($stringName){
# Create new instance
self::$instance = new self;
# Grabs stringName from an XML file
self::$string = $stringName;
# Return instance
var_dump(self::$instance);
return self::$instance;
}
public static function replace($replace_this, $with_this){
# Replace and return instance
self::$string = str_replace($replace_this, $with_this, self::$string);
return self::$instance;
}
public static function show(){
# Return String
return self::$string;
}
}
echo resources::getString("alpha") // alpha
->replace("lpha","bravo") // abravo
->replace("vo", resources::getString("charlie")->show()) // should be abracharlie
->show(); // charlie
I'd like it to understand why it's not working as I think it should and how it should actually work.
It seems that when I call the class again (despite var_dump saying its a seperate instance), it replaces the original text with "charlie" so I can't just replace a part of the first bit.
Thanks, Dominic
EDIT: Yes!! I have figured it out (using statics) but it seems Ryano below has an even better solution
<?php
class resources{
private static $instance, $string, $originalString;
public static function getInstance($stringName){
self::$instance = new self();
self::$originalString = $stringName;
return self::$instance;
}
public static function getString($stringName){
# Grabs stringName from an XML file
self::$string = $stringName;
return self::$instance;
}
function replace($replace_this, $with_this){
self::$originalString = str_replace($replace_this, $with_this, self::$originalString);
self::$string = self::$originalString;
return self::$instance;
}
function show(){
return self::$string;
}
}
echo resources::getInstance("alpha") // alpha
->replace("lpha","bravo") // abravo
->replace("vo", resources::getString("charlie")->show()) // should be abracharlie
->replace("lie", resources::getString("vo")->show()) // abracharvo
->show(); // abracharvo
echo "<br />";
echo resources::getInstance("randomer") // randomer
->replace("er","") // random
->replace("ran", resources::getString("")->show()) // dom
->replace("dom", resources::getString("Dom")->show()) // Dom
->show(); // Dom
echo "<br />";
echo resources::getInstance("nomster") // nomster
->replace("nom","nmo") // nmoster
->replace("nom", resources::getString("mon")->show()) // nmoster
->replace("nmo", resources::getString("mon")->show()) // monster
->show(); // monster
?>
Your problem is that everything is static. I would suggest brushing up on some object-oriented programming fundamentals.
Because everything is static, the state is shared between all invocations of the functions. In the line replace("vo", resources::getString("charlie")->show()), the nested call to resources::getString replaces the string built so far (abravo) with the argument to getString which is charlie. Then the wrapping function is called like replace("vo", "charlie"), but the value of self::$string is now charlie, which does not contain vo and therefore the final show() then returns simply charlie. If, instead of vo, you'd called it with replace("ar", resources::getString("charlie")->show()), the final show() would have instead returned chcharlielie.
You must create a class with non-static member variables and methods in order to maintain separate states.
Here's a working version:
class resources {
private $string;
public function __construct ($string) {
$this->string = $string;
}
public static function getString ($string) {
$obj = new resources($string);
return $obj;
}
public function replace ($replace_this, $with_this) {
# Replace and return instance
$this->string = str_replace($replace_this, $with_this, $this->string);
return $this;
}
public function show () {
# Return String
return $this->string;
}
}
Edit: I like the above code as the closest transition from the question's code. If I was writing something similar myself, I would simplify it further like this:
class Str {
private $str;
private function __construct ($str) {
$this->str = $str;
}
public static function with ($str) {
return new Str($str);
}
public function replace($replace_this, $with_this) {
$this->str = str_replace($replace_this, $with_this, $this->str);
return $this;
}
public function __toString () {
return $this->str;
}
}
echo Str::with('nomster')->replace('nom', 'mon') . "\n";
Now there's no need for show() and the names are a little nicer to type. Many other useful methods could be added here; any php string function you would want to chain.
When you call getString() several times, you create several instances since you call new self() in getString().
To prevent that from happening you should create a method getInstance() and use that in getString()
public static function getInstance() {
if(!self::instance) {
self::instance = new self();
}
return self::instance;
}
public static function getString() {
$instance = self::getInstance();
// use $instance here instead of self::instance
}

Categories