As the title states; how can I use the HTMLPurifier library inside my class?
I'm trying to get to grips with OOP and PHP classes for the first time and have successfully built a class that connects to my database using my database class and returns a blog article.
I would now like to parse the HTML markup for the blog article using HTMLPurifier but I would like to achieve this inside my blog class and I'm wondering how it can be achieved, as HTMLPurifier is a class.
My class so far:
namespace Blog\Reader;
use PDO;
use HTMLPurifier_Config; <--- trying to include it here
use \Database\Connect;
class BlogReader {
private static $instance = null;
private static $article = null;
private static $config = null;
private static $db = null;
private static function InitDB() {
if (self::$db) return;
try {
$connect = Connect::getInstance(self::$config['database']);
self::$db = $connect->getConnection();
} catch (Throwable $t) {}
}
private function __construct($config) {
self::$config = $config;
}
public static function getInstance($config) {
if (!self::$instance) {
self::$instance = new BlogReader($config);
}
return self::$instance;
}
public static function getArticle($id) {
self::InitDB();
try {
if (self::$db) {
$q = self::$db->prepare("
// sql
");
$q->bindValue(':id', (int) $id, PDO::PARAM_INT);
$q->execute();
self::$article = $q->fetchAll(PDO::FETCH_ASSOC);
//////////// <----- and trying to use it here
$HTMLPurifier_Config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($HTMLPurifier_Config);
///////////
} else {
throw new Exception("No database connection found.");
self::$article = null;
}
} catch (Throwable $t) {
self::$article = null;
}
return self::$article;
}
private function __clone() {}
private function __sleep() {}
private function __wakeup() {}
}
However, I get the following error log when trying anything like this:
Uncaught Error: Class 'HTMLPurifier_Config' not found in
....../php/classes/blog/reader/blogreader.class.php
And the line number of the error is on this line:
$HTMLPurifier_Config = HTMLPurifier_Config::createDefault();
My class directory structure:
[root]
[blog]
blog.php <--- using classes here
[php]
afs-autoload.php
[classes]
[blog]
[database]
[vendor]
[htmlpurifier-4.10.0]
[library]
HTMLPurifier.auto.php <--- this is what I used to `include` on blog.php to autoload HTMLPurifier_Config::createDefault() and new HTMLPurifier($purifier_config).
My Autoloader (afs-autoload.php) file:
define('CLASS_ROOT', dirname(__FILE__));
spl_autoload_register(function ($class) {
$file = CLASS_ROOT . '/classes/' . str_replace('\\', '/', strtolower($class)) . '.class.php';
if (file_exists($file)) {
require $file;
}
});
I literally started learning classes today, so I'm really baffled as to how I can achieve this, especially with the namespace system I used.
I hope somebody with better experience can guide me in the right direction.
Rewritten answer:
1) Your auto loader is looking for <class>.class.php files; but your HTMLPurifier_Config is in a HTMLPurifier.auto.php file.
2) Still in your autoloader: str_replace('\\' You do not need to escape characters when in single quotes, so this should be: str_replace('\'.
3) This excellent answer should help you learn when and how to use the use PHP keyword.
4) Your issue is not the scope of your use (I don't think you even need to use use). But is that your autoloader is looking for the wrong type of files. Try manually loading the class using require and seeing if it works properly.
Original Answer
namespace Blog\Reader;
use PDO;
use HTMLPurifier_Config;
What you're actually doing here is using the values within the defined namespace; so you're using:
Blog\Reader\HTMLPurifier_Config
If you're HTMLPurifier_Config file is within its own namespace you need to specify that so that the "use" grabs the right data!
If its not in its own namespace then it will be in the global namespace which is identified with a slash:
namespace Blog\Reader;
use PDO;
use \HTMLPurifier_Config;
If it is in the namespace HTMLPurifier, for example:
namespace Blog\Reader;
use PDO;
use \HTMLPurifier\HTMLPurifier_Config;
to load the correct data reference.
Just to wrap this up, if you are using namespaces inside a class which as been placed in a namespace, this is how you create your purifier objects:
$config = \HTMLPurifier_Config::createDefault();
$purifier = new \HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
You do not have to use a use command since the HTMLPurifier classes themselves are not in a namespace. But when your code is in a namespace, you need to pre-pend '\' to non-namespaced classes.
namespace \Tdiscus; // This is the line that makes the difference.
use \Tsugi\Util\Net;
class Threads {
...
$config = \HTMLPurifier_Config::createDefault();
...
$retval = new \stdClass();
...
$dbh = new \PDO($dsn, $user, $password);
}
Because you placed the class in a namespace, any "non-namespaced" classes need a "" prefix - like PDO and stdClass in the above example.
The HTMLPurifier folks could have put their classes in a namespace - but they chose not to do that.
Using namespaces is a common practice when developing libraries intended for use with composer. But HTMLPurifier existed before composer was in common use and their classes have a nice unique prefix because they started out in a global class namespace - so they decided to leave well enough alone and not break the non-dynamic loading / non-namespace legacy adopters of their library.
Related
I have the following code:
File: class.engine.php (The Main Engine of the website)
<?php
namespace Core;
class Engine {
}
File class.errors.php (The Error trigger if something happens, customized errors)
<?php
namespace Core\Errors;
class Errors {
public function TrigerError($e)
{
die($e);
}
}
File class.mysqli.php (The database connector)
<?php
namespace Core\MySQLiDrv;
class MySQLiDrv {
public $db = null;
public function __construct($database) {
$connector = mysqli_connect($database[0], $database[1], $database[2], $database[3]);
if(!$connector)
{
return new TrigerError("1");
}
}
}
File index.php (Where everything runs)
<?php
include("engine/class.engine.php");
include("engine/class.errors.php");
include("engine/class.mysqli.php");
$engine = new Engine();
$db = new MySQLiDrv(array("localhost", "user", "pass", "db"));
I want to extend Engine class with many multiple files and I want them to be in namespace, how can I do that properly, because I can't trigger the error in this case of the code.
And is it important in this case of code to use namespaces defined as something for easy use?
Your main issue as I see it is the lack of the USE statement and the fact that TrigerError is a method and not a class.
To fix these:
namespace Core\MySQLiDrv;
use Core\Errors\Errors;
class MySQLiDrv {
public $db = null;
public function __construct($database) {
$connector = mysqli_connect($database[0], $database[1], $database[2], $database[3]);
if(!$connector)
{
return (new Errors)->TrigerError("1");
}
}
}
Full working code:
namespace Core{
class Engine {
}
}
namespace Core\Errors{
class Errors {
public function TrigerError($e)
{
die($e);
}
}
}
namespace Core\MySQLiDrv{
use Core\Errors\Errors;
class MySQLiDrv {
public $db = null;
public function __construct($database) {
//$connector = mysqli_connect($database[0], $database[1], $database[2], $database[3]);
//if(!$connector)
//{
return (new Errors)->TrigerError("1");
//}
}
}
}
//default global namesapce
namespace {
use Core\Engine;
use Core\MySQLiDrv\MySQLiDrv;
$engine = new Engine();
$db = new MySQLiDrv(array("localhost", "user", "pass", "db"));
}
Output
1
Sandbox
The only real difference here is that when you use multiple namespaces in a single file (sandbox) you have to enclose the code with {}. You can remove those if these are separate files. Well that and I cant use the DB functions in the sandbox, but that is irrelevant in this example because we want to trigger the error.
However as said in the comments, I would look into Autoloading and Exceptions both of which will help you here.
I have an autoloader on github you can play around with if you want, it's similar to what Composer uses/does. Composer is just more standardized.
Autoloadig basically uses a combination of the namespace and class to find the file at run time (when the class is instantiated). So there are some naming conventions to be aware of, for example take your class Core\Errors\Errors this should be located in Core/Errors/Errors.php The namespace and the file path should be the same. What this does is you can eliminate all the include statements and this loads the files only when needed auto-magically.
I struggle with this problem for a while - and the reason is probably trivial.
Background
I've created parser module for my Yii2 application so I can call it from other places (mobile app, etc.) to get data from various websites. There may be many parser classes, all implementing same interface.
Project structure
...
/modules
\_ parser
\_components
\_parsers
\_SampleParser.php
\_controllers
\_DefaultController.php
\_Parser.php
...
I've removed some code for better readability.
DefaultController.php:
namespace app\modules\parser\controllers;
use Yii;
use yii\web\Controller;
use app\modules\parser\components\parsers;
use app\modules\parser\components\parsers\SampleParser;
/**
* Default controller for the `parser` module
*/
class DefaultController extends Controller
{
private function loadParser($parserName){
return new SampleParser(); // if I leave this here, everything works okay
$className = $parserName.'Parser';
$object = new $className();
if ($object instanceof IParseProvider){
return $object;
}
}
...
public function actionIndex()
{
$url = "http://google.com";
$parser = 'Sample';
$loadedParser = $this->loadParser($parser);
$response = $loadedParser->parse($url);
\Yii::$app->response->format = 'json';
return $response->toJson();
}
...
SampleParser.php:
<?php
namespace app\modules\parser\components\parsers;
use app\modules\parser\models\IParseProvider;
class SampleParser implements IParseProvider {
public function canParse($url){
}
public function parse($url){
}
}
Right now everything works more or less ok, so I guess I'm importing correct namespaces. But when I remove return new SampleParser(); and let the object to be created by string name, it fails with error:
PHP Fatal Error – yii\base\ErrorException
Class 'SampleParser' not found
with highlighted line:
$object = new $className();
What am I doing wrong here? Thanks!
Try again with help of Yii:
private function loadParser($parserName)
{
return \yii\di\Instance::ensure(
'app\modules\parser\components\parsers\\' . $parserName . 'Parser',
IParseProvider::class
);
}
Remember that ensure() throws \yii\base\InvalidConfigException when passed reference is not of the type you expect so you need to catch it at some point.
If you are using PHP < 5.5 instead of IParseProvider::class you can use full class name with it's namespace.
P.S. remove use app\modules\parser\components\parsers; unless you have got class named parsers you want to use.
recently I was learning zend framework 2, and there's a problem annoying me for a long time, things look like this:
<?php
namespace Album\Model;
// Add these import statements
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface
{
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
// Add content to these methods:
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
//....
?>
This code was a section of the "skeleton application" programme, which was a tutorial of ZF2. The first time I see the programme, I don't understand what's the usage of "namespace" and "use", because this two keyword doesn't exist in php5.2(also the same in the earlier edition), so I go to see the manual and try to understand it.I write a programme to simulate what really happens:
<?php
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the programme above works well, of course I created two folders named script and lib, and there's a file named test.php.
Seems like every thing is clear, zend framework also has a autoload function, BUT when I noticed the codes in "skeleton application programme", there was a namespace in the beginning, so I adds the namespace to my programme too:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the page returned me inforamtion as following:
Fatal error: Class 'script\lib\test' not found in E:\wamp\www\test\test_29.php on line 6
I tried to change the namespace's name such as script\lib, script\lib\test...
but it's useless.
Any answer will be appreciated, thanks.
Now I will give you more details about this issue:
To understand the usage of "namespace" and "use", I looked over the materials on php.net:
http://php.net/manual/en/language.namespaces.importing.php
In this page, there was a section of code looks like this:
Example #1 importing/aliasing with the use operator
<?php
namespace foo;
use My\Full\Classname as Another;
// this is the same as use My\Full\NSname as NSname
use My\Full\NSname;
// importing a global class
use ArrayObject;
$obj = new namespace\Another; // instantiates object of class foo\Another
$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // instantiates object of class ArrayObject
// without the "use ArrayObject" we would instantiate an object of class
?>
Now let's review the programme I write in the above:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
It's the same, I'm trying to simulate that instance, if we don't use the autoload function:
<?php
namespace test;
use script\lib\test;
require_once 'script/lib/test.php';
$o = new test();
echo $o->getWelcome();
?>
It works well too, BUT when I use __autoload function to load the class file, there's something wrong.
I don't konw where's problem, OR any body tried to write an instance to put the "Example #1" into practice? I will wait for your answer.
I think you're misunderstanding what's going on here.
Namespaces allow you to, more or less, create "directories" for your classes. So you can create the \Foo class and the \Test\Foo class (where \ represents the "root" of your application).
The way autoloading works is that your files mirror your namespacing. So foo.php would be in the root of your autoloading but you would create /test/foo.php for \Test\Foo
The use keyword has two uses. One is to alias class files and the other is, in PHP 5.4 or later, to bring in a Trait into your current class.
Now, to your question. First, Let's look at your code
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
This is confusing. You declare a namespace (which you don't need to do here) but then you alias it to script\lib\test. PHP is now looking for a file called /script/lib/test.php, which your error message says doesn't exist. But you said the file does exist so let's look at that
public function getWelcome() {
return 'welcome';
}
This isn't a class. It's a function. For this example you need a complete class
<?php
namespace script\lib;
class test {
public function getWelcome() {
return 'welcome';
}
}
Lastly, let's talk autoloading. You don't need to use use with autoloading. Your autoloader should take care of that for you. You should, however, use spl_autoload_register(), as __autoload() is soon to be depreciated.
From ZF2 docu
Zend\Loader\StandardAutoloader is designed as a PSR-0-compliant autoloader. It assumes a 1:1 mapping of the namespace+classname to the filesystem, wherein namespace separators and underscores are translated to directory separators.
Read more about: PSR-0
So if you're using namespaces the classname that gets send to the autoloader doesn't look like test. It looks like YOUR_NAMESPACE\test. YOUR_NAMESPACE is the namespace that you defined in the class with namespace YOUR_NAMESPACE;
PSR-0 is a standard that says: Your namespace should reflect your filesystem. You only have to replace the backslashes with forward slashes. Or _ with / if you're using pseudo namespaces like in ZF1. (Album_Model_Album)
So output the $className that is sent to your autoloader and you will see..
I'm trying to create a system that it has a GeneralObj. The GeneralObj allows user to initiate special objects for database's tables with a String of the table name. So far, it works perfect.
class GeneralObj{
function __construct($tableName) {
//...
}
}
However, it is too tired to type new GeneralObj(XXX) every time.
I am wondering is that possible to simplify the process to like new XXX(), which is actually running the same as new GeneralObj(XXX)?
I spot PHP provided __autoload method for dynamic loading files in the setting include_path but it requires a the actually definition file existing. I really don't want to copy and copy the same definition files only changing a little.
For cause, eval is not an option.
Maybe you can just auto-create the files in the autoloader:
function __autoload($class_name) {
// check for classes ending with 'Table'
if (preg_match('/(.*?)Table/', $class_name, $match)) {
$classPath = PATH_TO_TABLES . '/' . $match[1] . '.php';
// auto-create the file
if (!file_exists($classPath)) {
$classContent = "
class $class_name extends GeneralObj {
public __construct() {
parent::__construct('{$match[1]}');
}
}";
file_put_contents($classPath, $classContent);
}
require_once $classPath;
}
}
Use inheritance. Make GeneralObj the superclass of the table specific classes. This way you can dynamically derive class names and instantiate objects. Example:
class someTable extends GeneralObj {
}
$tableName = 'some';
$className = $tableName . 'Table';
$obj = new $className;
No, this is not possible.
The runkit extension allows programmatic manipulation of the PHP runtime environment, but it cannot do this. Even if it could, it would IMHO be a very bad idea, greatly impacting the requirements and complexity of the application in exchange for saving a few keystrokes.
In an unrelated note, your GeneralObj class has functionality that sounds suspiciously like that of a dependency injection container. Perhaps you should consider replacing it with one?
Something like this autoloader:
myAutoloader::Register();
class myAutoloader
{
/**
* Register the Autoloader with SPL
*
*/
public static function Register() {
if (function_exists('__autoload')) {
// Register any existing autoloader function with SPL, so we don't get any clashes
spl_autoload_register('__autoload');
}
// Register ourselves with SPL
return spl_autoload_register(array('myAutoloader', 'Load'));
} // function Register()
/**
* Autoload a class identified by name
*
* #param string $pClassName Name of the object to load
*/
public static function Load($pClassName){
if (class_exists($pClassName,FALSE)) {
// Already loaded
return FALSE;
}
$pClassFilePath = str_replace('_',DIRECTORY_SEPARATOR,$pClassName) . '.php';
if (file_exists($pClassFilePath) === FALSE) {
// Not a class file
return new GeneralObj($pClassName);
}
require($pClassFilePath);
} // function Load()
}
And it's up to GeneralObj to throw an exception if the table class can't be instantiated
I work a lot in PHP but I never really understand the namespace method in PHP. Can somebody help me here? I have read on php.net's website its not explained good enough, and I can't find examples on it.
I need to know how I can make code in sample version.
namespace: sample
class: sample_class_1
function: test_func_1
class: sample_class_2
function: test_func_2
function: test_func_3
Like this?
<?php
namespace sample
{
class Sample_class_1
{
public function test_func_1($text)
{
echo $text;
}
}
class Sample_class_2
{
public static function test_func_2()
{
$c = new Sample_class_1();
$c->test_func_1("func 2<br />");
}
public static function test_func_3()
{
$c = new Sample_class_1();
$c->test_func_1("func 3<br />");
}
}
}
// Now entering the root namespace...
// (You only need to do this if you've already used a different
// namespace in the same file)
namespace
{
// Directly addressing a class
$c = new sample\Sample_class_1();
$c->test_func_1("Hello world<br />");
// Directly addressing a class's static methods
sample\Sample_class_2::test_func_2();
// Importing a class into the current namespace
use sample\Sample_class_2;
sample\Sample_class_2::test_func_3();
}
// Now entering yet another namespace
namespace sample2
{
// Directly addressing a class
$c = new sample\Sample_class_1();
$c->test_func_1("Hello world<br />");
// Directly addressing a class's static methods
sample\Sample_class_2::test_func_2();
// Importing a class into the current namespace
use sample\Sample_class_2;
sample\Sample_class_2::test_func_3();
}
If you're in another file you don't need to call namespace { to enter the root namespace. So imagine the code below is another file "ns2.php" while the original code was in "ns1.php":
// Include the other file
include("ns1.php");
// No "namespace" directive was used, so we're in the root namespace.
// Directly addressing a class
$c = new sample\Sample_class_1();
$c->test_func_1("Hello world<br />");
// Directly addressing a class's static methods
sample\Sample_class_2::test_func_2();
// Importing a class into the current namespace
use sample\Sample_class_2;
sample\Sample_class_2::test_func_3();