Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced PHP Programming

.pdf
Скачиваний:
67
Добавлен:
14.04.2015
Размер:
7.82 Mб
Скачать

118 Chapter 4 Implementing with PHP: Templates and the Web

$smarty->assign(name, $_COOKIE[name]);

}

$smarty->display(homepage.tpl, md5($_COOKIE[name]));

Notice that you can still use is_cached() by passing the cache key into that.

Be aware that Smarty has no built-in garbage collection and that every cached page results in a file being stored on your cache filesystem.This opens you to both accidental and malicious denial-of-service attacks if you have thousands of cached pages accumulated on the filesystem. Selectively caching files based on a key with a relatively low number of possible values is recommended.

A better solution for caching files that have highly dynamic content is to cache everything except the dynamic content.You want to be able to use code like this in your templates:

{* homepage.tpl *}

{* static content that can be cached *}

{nocache}

Hello {$name}!

{/nocache}

{* other static content *}

To accomplish this, you can register a custom block handler for the nocache block via the Smarty method register_block().The block-handling function itself takes three parameters: any parameters passed into the tag, the content enclosed by the block, and the Smarty object.

The function you want to implement simply returns the block content unchanged, as shown here:

function nocache_block($params, $content, Smarty $smarty)

{

return $content;

}

The trick is to register the function nocache_block as uncacheable.You do this by setting the third parameter of register_block() to false, as follows:

$smarty->register_block(nocache, nocache_block, false);

Now even in templates that are to be cached, the enclosed nocache block will always be dynamically generated.

Be aware that if you use is_cached() to short-circuit your prep work, you need to make sure you unconditionally perform the setup for the uncacheable block.

Advanced Smarty Features

As a final point in this whirlwind coverage of Smarty, some additional features are worth noting:

Smarty 119

nSecurity settings—Smarty can be configured to allow the use of only certain functions and modifiers and to disallow the use of php blocks. It is good practice to disable php blocks immediately and to always think twice before enabling them. Security is globally enabled by setting the Smarty class attribute $security to true. After that is done, individual security settings are toggled via the attribute $security_settings. See the Smarty manual for complete details.The best way

to enable security is to simply set that attribute in the class constructor, as shown

here for Smarty_ExampleOrg:

class Smarty_Example_Org extends Smarty {

function _ _construct()

{

$this->Smarty();

$this->template_dir = /data/www/www.example.org/templates;

$this->config_dir

= /data/www/www.example.org/smarty_config;

$this->compile_dir

= /data/cachefiles/www.example.org/templates_c;

$this->cache_dir

= /data/cachefiles/www.example.org/smarty_cache;

$this->security

= true;

}

}

nTemplate prefilter—Template prefilters allow you to register a function that is run on the template before it is compiled.The standard example is a prefilter to remove all unnecessary whitespace from a template. Prefilters are registered via the method register_prefilter().

nTemplate postfilter—A template postfilter is run on a template after it is compiled but before it is written to disk. An ideal use of a postfilter is to add some stock PHP code to every compiled template; for example, code that sets HTTP headers that invoke session_start(). Postfilters are registered via the method register_postfiler(). Here is a simple postfilter which ensures that session_start() is enabled:

function add_session_start($tpl_source, Smarty $smarty)

{

return <?php session_start(); ?>\n.$tpl_source;

}

$smarty = new Smarty_ExampleOrg; $smarty->register_postfilter(add_session_start);

nOutput filters—This function is run on any Smarty-generated output before it is

sent to the browser (or written to the Smarty cache).This is an ideal place to perform any last-minute data munging before content is sent out. Examples of output filters include rewriting all email addresses in output as george at omniti.com (to

120 Chapter 4 Implementing with PHP: Templates and the Web

cut down on email-hunting Web spiders) or replacing all text emoticons such as :) with links to actual emoticon images. Output filters are registered with register_outputfilter().

nCache handlers—You can register custom cache back ends that allow you to alter the way Smarty reads and writes its cache files.This is useful if you want Smarty to use a database to store its cache files and compiled templates to guarantee that all servers serve identical cached content. Cache handlers are registered by setting the Smarty class attribute $cache_handler_func.

nCustomizable tags—If you don’t like {} as delimiters, you can change them to whatever you want. I prefer the XML-ish <smarty></smarty>.

Writing Your Own Template Solution

If your development and design teams have the self-discipline to separate display and application logic without any language-level enforcement of the separation, then using plain PHP as an ad hoc template system is a good solution. PHP began as a template language, designed to glue various C functions together to make HTML pages. Although PHP has evolved from a simple glue language into a versatile general-purpose scripting language, it has remained true to its roots and still excels at templating.

The basic strategy is to write templates that are like compiled Smarty templates. Here is a basic class to handle rendering templates:

class Template { public $template_dir;

function display($file) { $template = $this;

// suppress non-existent variable warnings error_reporting(E_ALL ~ E_NOTICE); include($this->template_dir.$file);

}

}

To use this template class, you create a new Template object, populate it with the data you want, and call display().The Template object itself will be visible as $template. The hello template for this class looks like this:

<html>

<title><?php echo $template->title ?></title>

<body>

Hello <?php echo $template->name ?>!

</body>

</html>

Further Reading

121

The PHP to call the template is as follows:

$template = new Template;

$template->template_dir = /data/www/www.example.org/templates/;

$template->title = Hello World;

$template->name = array_key_exists(name, $_GET)?$_GET[name]:Stranger;

$template->display(default.tmpl);

As with Smarty, with PHP you can encapsulate default data in the class constructor, as shown here:

class Template_ExampleOrg extends Template

{

public function _ _construct()

{

$this->template_dir = /data/www/www.example.org/templates/; $this->title = www.example.org;

}

}

Because templates are executed with the PHP function include(), they can contain arbitrary PHP code.This allows you to implement all your display logic in PHP. For example, to make a header file that imports CSS style sheets from an array, your code would look like this:

<!-- header.tpl -->

<html>

<head><title><?php echo $template->title ?></title>

<?php foreach ($template->css as $link) { ?>

<link rel=stylesheettype=text/csshref=<?php echo $link ?>”” />

<?php } ?>

</head>

This is an entirely appropriate use of PHP in a template because it is clearly display logic and not application logic. Including logic in templates is not a bad thing. Indeed, any nontrivial display choice requires logic.The key is to keep display logic in templates and keep application logic outside templates.

When you use the same language to implement both display and application logic, you must take extra care to maintain this separation. I think that if you cannot rigidly enforce this standard by policy, you have a seriously flawed development environment.

Any language can be misused; it is better to have users willingly comply with your standards than to try to force them to.

Further Reading

This chapter barely scratches the surface of Smarty’s full capabilities. Excellent Smarty documentation is available at the Smarty Web site, http://smarty.php.net.

122 Chapter 4 Implementing with PHP: Templates and the Web

There are a number of template systems in PHP. Even if you are happy with Smarty, surveying the capabilities of other systems is a good thing. Some popular template alternatives include the following:

nHTML_Template_IT, HTML_Template_ITX, and HTML_Template_Flexy—All available from PEAR (http://pear.php.net)

nTemplateTamer—Available at http://www.templatetamer.com

nSmartTemplate—Available at http://www.smartphp.net

If you don’t know Cascading Style Sheets (CSS), you should learn it. CSS provides an extremely powerful ability to alter the way HTML is formatted in modern browsers. CSS keeps you from ever using FONT tags or TABLE attributes again.The master page for the CSS specification is available at http://www.w3.org/Style/CSS.

Dynamic HTML:The Definitive Reference by Danny Goodman is an excellent practical reference for HTML, CSS, JavaScript, and Document Object Model (DOM).

5

Implementing with PHP:

Standalone Scripts

THIS CHAPTER DESCRIBES HOW TO REUSE EXISTING code libraries to perform adminis-

trative tasks in PHP and how to write standalone and one-liner scripts. It gives a couple extremely paradigm-breaking projects that put PHP to use outside the Web environment.

For me, one of the most exciting aspects of participating in the development of PHP has been watching the language grow from the simple Web-scripting-specific language of the PHP 3 (and earlier) days into a more robust and versatile language that also excels at Web scripting.

There are benefits to being an extremely specialized language:

nIt is easy to be the perfect tool for a given job if you were written specifically to do that job.

nIt is easier to take over a niche than to compete with other, more mature, generalpurpose languages.

On the other hand, there are also drawbacks to being an extremely specialized language:

nCompanies rarely focus on a single niche to the exclusion of all others. For example, even Web-centric companies have back-end and systems scripting requirements.

nSatisfying a variety of needs with specialist languages requires developers to master more than one language.

nCommon code gets duplicated in every language used.

As a Web professional, I see these drawbacks as serious problems. Duplicated code means that bugs need to be fixed in more than one place (and worse, in more than one

124 Chapter 5 Implementing with PHP: Standalone Scripts

language), which equates with a higher overall bug rate and a tendency for bugs to live on in lesser-used portions of the code base. Actively developing in a number of languages means that instead of developers becoming experts in a single language, they must know multiple languages.This makes it increasingly hard to have really good programmers, as their focus is split between multiple languages. Alternatively, some companies tackle the problem by having separate programmer groups handle separate business areas. Although that can be effective, it does not solve the code-reuse problem, it is expensive, and it decreases the agility of the business.

Pragmatism

In their excellent book The Pragmatic Programmer: From Journeyman to Master, David Thomas and Andrew Hunt suggest that all professional programmers learn (at least) one new language per year. I agree wholeheartedly with this advice, but I often see it applied poorly. Many companies have a highly schizophrenic code base, with different applications written in different languages because the developer who was writing them was learning language X at the time and thought it would be a good place to hone his skills. This is especially true when a lead developer at the company is particularly smart or driven and is able to juggle multiple languages with relative ease.

This is not pragmatic.

The problem is that although you might be smart enough to handle Python, Perl, PHP, Ruby, Java, C++, and C# at the same time, many of the people who will be working on the code base will not be able to handle this. You will end up with tons of repeated code. For instance, you will almost certainly have the same basic database access library rewritten in each language. If you are lucky and have foresight, all the libraries will at least have the same API. If not, they will all be slightly different, and you will experience tons of bugs as developers code to the Python API in PHP.

Learning new languages is a good thing. I try hard to take Thomas and Hunt’s advice. Learning languages is important because it expands your horizons, keeps your skills current, and exposes you to new ideas. Bring the techniques and insights you get from your studies with you to work, but be gentle about bringing the actual languages to your job.

In my experience, the ideal language is the one that has a specialist-like affinity for the major focus of your projects but is general enough to handle the peripheral tasks that arise. For most Web-programming needs, PHP fills that role quite nicely.The PHP development model has remained close to its Web-scripting roots. For ease of use and fit to the “Web problem,” it still remains without parallel (as evidenced by its continually rising adoption rate). PHP has also adapted to fill the needs of more general problems as well. Starting in PHP 4 and continuing into PHP 5, PHP has become aptly suited to a number of non-Web-programming needs as well.

Is PHP the best language for scripting back-end tasks? If you have a large API that drives many of your business processes, the ability to merge and reuse code from your Web environment is incredibly valuable.This value might easily outweigh the fact that Perl and Python are more mature back-end scripting languages.

Handling Input/Output (I/O)

125

Introduction to the PHP Command-Line Interface (CLI)

If you built PHP with --enable-cli, a binary called php is installed into the binaries directory of the installation path. By default this is /usr/local/bin.To prevent having to specify the full path of php every time you run it, this directory should be in your PATH environment variable.To execute a PHP script phpscript.php from the command line on a Unix system, you can type this:

> php phpscript.php

Alternatively, you can add the following line to the top of your script:

#!/usr/bin/env php

and then mark the script as executable with chmod, as follows:

> chmod u+rx phpscript.php

Now you can run phpscript.php as follows:

>./phpscript.php

This #! syntax is known as a “she-bang,” and using it is the standard way of making script executables on Unix systems.

On Windows systems, your registry will be modified to associate .php scripts with the php executable so that when you click on them, they will be parsed and run. However, because PHP has a wider deployment on Unix systems (mainly for security, cost, and performance reasons) than on Windows systems, this book uses Unix examples exclusively.

Except for the way they handle input, PHP command-line scripts behave very much like their Web-based brethren.

Handling Input/Output (I/O)

A central aspect of the Unix design philosophy is that a number of small and independent programs can be chained together to perform complicated tasks.This chaining is traditionally accomplished by having a program read input from the terminal and send its output back to the terminal.The Unix environment provides three special file handles that can be used to send and receive data between an application and the invoking user’s terminal (also known as a tty):

n stdin—Pronounced “standard in” or “standard input,” standard input captures any data that is input through the terminal.

n stdout—Pronounced “standard out” or “standard output,” standard output goes directly to your screen (and if you are redirecting the output to another program, it is received on its stdin).When you use print or echo in the PHP CGI or CLI, the data is sent to stdout.

126 Chapter 5 Implementing with PHP: Standalone Scripts

n stderr—Pronounced “standard error,” this is also directed to the user’s terminal, but over a different file handle than stdin. stderr generated by a program will not be read into another application’s stdin file handle without the use of output redirection. (See the man page for your terminal shell to see how to do this; it’s different for each one.)

In the PHP CLI, the special file handles can be accessed by using the following constants:

nSTDIN

nSTDOUT

nSTDERR

Using these constants is identical to opening the streams manually. (If you are running the PHP CGI version, you need to do this manually.) You explicitly open those streams as follows:

$stdin = fopen(php://stdin, r); $stdout = fopen(php://stdout, w); $stderr = fopen(php://stderr, w);

Why Use STDOUT?

Although it might seem pointless to use STDOUT as a file handle when you can directly print by using print/echo, it is actually quite convenient. STDOUT allows you to write output functions that simply take stream resources, so that you can easily switch between sending your output to the user’s terminal, to a remote server via an HTTP stream, or to anywhere via any other output stream.

The downside is that you cannot take advantage of PHP’s output filters or output buffering, but you can register your own streams filters via streams_filter_register().

Here is a quick script that reads in a file on stdin, numbers each line, and outputs the result to stdout:

#!/usr/bin/env php <?php

$lineno = 1;

while(($line = fgets(STDIN)) != false) { fputs(STDOUT, $lineno $line);

$lineno++;

}

?>

Handling Input/Output (I/O)

127

When you run this script on itself, you get the following output:

1 #!/usr/bin/env php

2 <?php

3

4 $lineno = 1;

5 while(($line = fgets(STDIN)) != false) {

6fputs(STDOUT, $lineno $line);

7$lineno++;

8}

9?>

stderr is convenient to use for error notifications and debugging because it will not be read in by a receiving program’s stdin.The following is a program that reads in an Apache combined-format log and reports on the number of unique IP addresses and browser types seen in the file:

<?php

$counts = array(ip=> array(), user_agent=> array()); while(($line = fgets(STDIN)) != false) {

# This regex matches a combined log format line field-by-field. $regex = /^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] .

‘“(\S+) (.*?) (\S+)(\S+) (\S+) ([^]*)” “([^]*)$/; preg_match($regex,$line,$matches);

list(, $ip, $ident_name, $remote_user, $date, $time, $gmt_off, $method, $url, $protocol, $code, $bytes, $referrer, $user_agent) = $matches;

$counts[ip][$ip]++; $counts[user_agent][$user_agent]++;

# Print a .to STDERR every thousand lines processed. if(($lineno++ % 1000) == 0) {

fwrite(STDERR, .);

}

}

arsort($counts[ip], SORT_NUMERIC); reset($counts[ip]); arsort($counts[user_agent], SORT_NUMERIC); reset($counts[user_agent]);

foreach(array(ip, user_agent) as $field) {

$i = 0;

print Top number of requests by $field\n;

print --------------------------------

\n;

foreach($counts[$field] as $k => $v) {

 

print $v\t\t$k\n;

 

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]