Entering Doctypes - php

I have a Document class that is responsible for holding the document that will eventually be gzipped and then printed onto the page. In that Document holds the document's DocType. The Problem I am facing is how to correctly implement displaying the Doctype.
For example: I could create an array of popular Doctypes that are the most frequently used, and then if you enter 'strict', it would automatically pick HTML 4.01 Strict. If there isn't a known DocType, it could assume you are entering in a custom DocType, and just display what you've entered. This seems a error prone and clumbsy.
$document->setDoctype("strict");
..... in a class far, far, away........
function setDocType($type)
{
if(in_array($type, $this->doctypes))
{
$this->doctype = $this->doctypes[$type];
}
else
{
$this->doctype = $type;
}
}
Another way to do it would be to simply have to enter in the entire DocType every time.
$document->setDoctype('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">');
How would you deal with this problem?

It depends if you want to allow entering custom doctypes. If you consider that situation an exception you should probably throw an exception when someone tries using undefined doctype. You could also provide a method for defining a custom doctype and force using that method before setting the actual doctype.
I would do something like this:
class Document {
private $doctypes = array(
'html4' => '...',
'xhtml1strict' => '...'
);
private $doctype = null;
public function addCustomDocType($name, $definition) {
if (empty($this->doctypes[$name])) {
$this->doctypes[$name] = $definition;
} else {
throw new Exception('Definition already exists');
}
}
public function setDocType($name) {
if (!empty($this->doctypes[$name])) {
$this->doctype = $this->doctypes[$name];
} else {
throw new Exception('Doctype not found');
}
}
}
The second solution where you must type doctype definition is IMO ugly and I would avoid it.

I think you should just checkout the doctype w3c list.
http://www.w3.org/QA/2002/04/valid-dtd-list.html

Are users allowed to enter a custom doctype?
Perhaps create a base class
Doc
And extend it to each type, ex:
StrictDoc extends Doc
etc.
And it'll set its own doc type for each. If you want to add to the doc types, simply create another class that extends Doc.

Related

Best way to handle static text / messages in PHP OOP project (JSON maybe?)

Until now, unless I made a multilingual website (where I would use .mo & .po files), all the text would be scrambled all around the template and / or class files. Instead, I would like to store all static text in a file that is easily editable by my coworkers and clients (that rules out database storage and POedit).
I made a JSON file that stores the messages / static text like this:
{
"titles": {
"main_title": "This is the main title of the website",
"login_page_title": "Please, sing in",
"about_page_title": "About us"
},
"errors": {
"empty_required_field": "This field is required.",
"database_connection_error": "Couldn't connect to the database.",
}
}
Then I import it in the index.php file:
$messages = json_decode(file_get_contents("messages.json"));
And use it like:
echo($messages->titles->main_title);
Which has been working so far so good (although I'm uncertain that there aren't better ways to archieve this). At least in the template pages where everything is html with minimal logic.
But I'm having trouble using the strings from the JSON file inside the classes' functions. I would like to use the error messages when throwing exceptions, for example. But I'm quite reluctant about stating "global $message" in every function where it's used (feels repetitive). Also everybody says that globals are naughty.
So my questions are two:
1) Is the JSON file a good way to handle my problem? (and if not, why, and which method would be better?).
2) How could I retrieve the stored strings from inside the classes? I'm thinking something like extending the Exception class to include the error messages, but I'm unsure of how to do it.
Thanks in advance for your help.
One approach, which Laravel takes, is creating some sort of directory tree like the following:
lang/
en/
titles.php
errors.php
titles.php could contain the following:
<?php
return [
'main_title' => 'This is the main title of the website',
'login_page_title' => 'Please, sing in',
'about_page_title' => 'About us'
];
As for errors.php:
<?php
return [
'empty_required_field' => 'This field is required.',
'database_connection_error' => "Couldn't connect to the database.",
];
I don't really like the JSON approach because it's not very flexible. For one, in PHP files, you have access to any variables you may want to give it, there's comments, possibility of using functions to create some messages, etc. This is why I recommend the above method.
In order to get the messages, you would require the file in a variable, like $titles = require 'lang/en/titles.php', using it like: $titles['main_title']. This method also makes it easy to change the language if needed.
While I'm not 100% sure I understand your exception problem, you would throw an exception with the appropriate message like: throw new Exception($errors['empty_required_field']);
In the end I opted for a Singleton class that loads/includes a separate text file. Nice global scope and should be easy to adapt to other needs (multilingüal, separate language files, or whatever). As I said I'm no expert so all critique is welcome.
<?php
class CustomText {
private static $instance = null;
private static $text;
private function __clone() {}
// On construct, checks if the strings are stored in a session.
// If not, retrieves them from file and stores them in a session.
private function __construct() {
if(self::isStoredInSession() == true) {
self::$text = $_SESSION["custom_text"];
} else {
//self::$text = json_decode(file_get_contents("messages.json"),true);
self::$text = include_once("messages.php");
self::saveToSession();
}
}
// Private initialization called on every public method so I don't have to worry about it on other files.
private static function initialize() {
if(self::$instance == null) {
self::$instance = new self;
}
}
// Session management
private static function saveToSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION["custom_text"])) {
$_SESSION["custom_text"] = self::$text;
}
}
private static function isStoredInSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(isset($_SESSION["custom_text"])) {
return true;
}
return false;
}
// Sample public functions
public static function getText($section,$string){
self::initialize();
if(isset(self::$text[$section][$string])) {
return self::$text[$section][$string];
} else {
return "";
}
}
public static function getError($string) {
self::initialize();
if(isset(self::$text["error"][$string])) {
return self::$text["error"][$string];
} else {
return "";
}
}
public static function getWebsiteTitle($section,$divider = " - ") {
self::initialize();
$title = "";
if(isset(self::$text["title"]["main"])) {
$title .= self::$text["title"]["main"];
}
if(isset(self::$text["title"][$section])) {
if(!empty($title)) {
$title .= $divider;
}
$title .= self::$text["title"][$section];
}
return $title;
}
}
What worries me the most is that I'm not sure that storing the data in a session is better that including a file on each page, and I have everything twice in the session variable and the class parameter.

CakePHP: How to use a view element inside of a controller

I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php

Request for IRC URI Scheme for HTML Purifier 4.2.0

Can someone help me to establish using IRC URI Scheme for HTML Purifier 4.2.0? I can't seem to figure out how to configure or which files to modify so that purified html allows for irc:// links.
Is it possible I can simply modify configuration within the following code block?
require_once "htmlpurifier-4.2.0/library/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set("HTML.Doctype", "XHTML 1.0 Strict");
$purifier = new HTMLPurifier($purifier_config);
Update:
I edited library/standalone/HTMLPurifier/ConfigSchema/schema.ser changing both instances of "4:nntp" to "3:irc" and found error:
Warning: Directory htmlpurifier-4.2.0/library/standalone/HTMLPurifier/DefinitionCache/Serializer/URI not writable, please chmod to 777
I believe this will help to establish support for IRC URI Scheme after making this change. I'll report back in a bit.
Hmm, after making it writable, no error appeared, but no results =\
HTML Purifier doesn't seem to have a native support for the IRC scheme. But: Have you tried something like this? Put this in /library/HTMLPurifier/URIScheme, or otherwise make sure that autoloading finds it:
class HTMLPurifier_URIScheme_irc extends HTMLPurifier_URIScheme {
public $default_port = 6667;
public $browsable = false;
public function validate(&$uri, $config, $context) {
if (parent::validate($uri, $config, $context) === false) {
return false;
}
if (!is_null($uri->path)) {
// get channel name
$uri->path = array_shift(explode('/', $uri->path));
}
$uri->userinfo = null;
$uri->query = null;
$uri->fragment = null;
return true;
}
}
...and change your configuration with...
$purifier->config->set(
'URI.AllowedSchemes',
array('irc' => true, /* ... other schemes here ... */)
);
That may not work out of the box, but I'm thinking that should be the right direction...

Best way to handle menu not found exception?

I'm currently refactoring the menu class in our PHP CMS, and am currently trying to work out the best way to handle the issue where someone tries to create a menu (by passing in the title of the menu (we have main, footer, utility etc menus), but the menu isn't in the database.
If they try to create a menu object with a menu that can be found then there's no problem, I can return the object as requested. If they try to create one that isn't found though, I'm currently throwing an exception (which causes an email to be sent), then creating a blank menu object and returning that. The call to output the menu then works without error, but outputs nothing.
(I've done the above by setting it up so a static method of the menu class is called to create a menu object, which can then handle throwing an exception if necessary and return either the requested menu object, or a blank one).
Hopefully all that makes sense! Is this the best method to take? Or is there a more elegant solution?
Chris
Edit:
Here's the static function which is called to create a menu:
static function makeMenu($id,$breakDepth=1){
// try to create Menu
try {
$menu = new Menu($id, $breakDepth);
}
catch (no_menu_found_exception $e) {
// if we failed to find it, an email should have been sent, and create a blank menu so rest of site works without error
$menu = new Menu("");
}
return $menu;
}
and here's the constructor:
function __construct($id,$breakDepth=1){
$this->treeObject = Doctrine_Core::getTable('CmsMenuItemNew')->getTree();
if ($id == "") {
// if ID supplied is empty, return an empty menu object
$this->rootNode = null;
$this->name = $id;
return;
}
if (is_numeric($id)) {
// check it exists?
$this->rootNode = $id;
$node = Doctrine_Core::getTable('CmsMenuItemNew')->findByDQL("menuid = '".$id."'")->getFirst();
$this->name = $node->menutitle;
if ($this->name == "") $this->rootNode = null;
return;
} else {
$this->name = $id;
// find the menu ID for the supplied name
$table = Doctrine_Core::getTable('CmsMenuItemNew');
$table->setOption("orderBy", "level");
$this->rootNode = $table->findByDQL("menutitle = '$id'")->getFirst()->menuid;
// rootNode with supplied name not found, so look for a branch in the main menu
$this->breakDepth = $breakDepth;
if ($this->rootNode === null) {
throw new no_menu_found_exception("Menu not found: ".$id);
}
}
}
as mentioned - its still in development, so not completely finished yet.
Building a blank object is a nice thing. The right design pattern for that is called SpecialObjects. To be complete your code should return a MenuNotFound object which have the same interface as MenuObject.
Then the way this MenuNotFound objects reacts to the interface entry points is up to you.
This avoid checks on the type of object returned and permits chaining.
For the Exception I personnaly prefer Exception which are only real problems. But in your case if you want to get a mail the exception is not a bad idea, maybe this exception or mail handling could be done in the MenuNotFound init.

Problems with redeclaring classes when using PHP's class_alias in a functional test

I'm using PHP 5.3's class_alias to help process my Symfony 1.4 (Doctrine) forms. I use a single action to process multiple form pages but using a switch statement to choose a Form Class to use.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
class_alias('MyFormPage1Form', 'FormAlias');
break;
...
}
$this->form = new FormAlias($obj);
}
This works brilliantly when browsing the website, but fails my functional tests, because when a page is loaded more than once, like so:
$browser->info('1 - Edit Form Page 1')->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end()->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end();
I get a 500 response to the second request, with the following error:
last request threw an uncaught exception RuntimeException: PHP sent a warning error at /.../apps/frontend/modules/.../actions/actions.class.php line 225 (Cannot redeclare class FormAlias)
This makes it very hard to test form submissions (which typically post back to themselves).
Presumably this is because Symfony's tester hasn't cleared the throughput in the same way.
Is there a way to 'unalias' or otherwise allow this sort of redeclaration?
As an alternate solution you can assign the name of the class to instantiate to a variable and new that:
public function executeEdit(sfWebRequest $request) {
$formType;
switch($request->getParameter('page')) {
case 'page-1':
$formType = 'MyFormPage1Form';
break;
...
}
$this->form = new $formType();
}
This doesn't use class_alias but keeps the instantiation in a single spot.
I do not know for sure if it is possible, but judging from the Manual, I'd say no. Once the class is aliased, there is no way to reset it or redeclare it with a different name. But then again, why do use the alias at all?
From your code I assume you are doing the aliasing in each additional case block. But if so, you can just as well simply instantiate the form in those blocks, e.g.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
$form = new MyFormPage1Form($obj);
break;
...
}
$this->form = $form;
}
You are hardcoding the class names into the switch/case block anyway when using class_alias. There is no advantage in using it. If you wanted to do it dynamically, you could create an array mapping from 'page' to 'className' and then simply lookup the appropriate class.
public function executeEdit(sfWebRequest $request) {
$mapping = array(
'page-1' => 'MyFormPage1Form',
// more mappings
);
$form = NULL;
$id = $request->getParameter('page');
if(array_key_exists($id, $mapping)) {
$className = $mapping[$id];
$form = new $className($obj);
}
$this->form = $form;
}
This way, you could also put the entire mapping in a config file. Or you could create FormFactory.
public function executeEdit(sfWebRequest $request) {
$this->form = FormFactory::create($request->getParameter('page'), $obj);
}
If you are using the Symfony Components DI Container, you could also get rid of the hard coded factory dependency and just use the service container to get the form. That would be the cleanest approach IMO. Basically, using class_alias just feels inappropriate here to me.
function class_alias_once($class, $alias) {
if (!class_exists($alias)) {
class_alias($class, $alias);
}
}
This doesn't solve the problem itself, but by using this function it is ensured that you don't get the error. Maybe this will suffice for your purpose.

Categories