2010年6月21日

以下内容针对使用PHP的session_set_save_handler的托管机制进行PHP会话管理托管的开发人员。


假定我们通过open, close, pick, dump, clear, gc六个函数对PHP的会话管理进行托管。PHP的session的gc回收机制本身没有问题,问题在于对于未进行gc回收,而又已经过时了的session。


PHP应用中,会话管理都可以依赖于传统的gc机制进行回收,然而当一个项目的并发越高,而gc回收的几率越高,对于数据库(或者IO)而言是一大负荷,所以面对并发高的站点,我们都会将gc回收的基数增加(ini_set('session.gc_divisor', xxxx);),这个也不是本文要讨论的问题重点,所以具体就不讨论了。


当尝试对会话进行托管控制,实际上一方面是为了减轻PHP会话机制本身的一些弊端(减少在$_SESSION变量中存放重要的用户信息),其次,也能帮助我们更好的掌握和管理整个系统的会话。


一个标准的设计,会话数据本身,会为其设计一个生命周期,当超过声明周期的会话,当用户的客户端再持有该session_id而发生会话处理时,则认为该session已经无效,用户当重新登录以维持与服务端的会话连接。

 

 

$sessName = "MY_APP_SID";
session_name($sessName);
session_start(); // => 此处将立刻调用刚才托管的open和pick两个函数

 

 

 


对于一个open函数:


 

function open($sessId) {
$sess = /* 根据$sessId获取到会话数据 */;
if ($sess->isValid()) { /* 检查会话是否有效 */
return $anyVal;
}
return false;
}

 

 

 

open函数,就是读取该客户端持有的sessId,并且读取该会话的value值($_SESSION中的值),这中间PHP会内部将$anyVal进行session_decode()返回到$_SESSION中。


当一个会话的isValid返回false的时候,我们会需要重新生成会话id,并且通知客户端更新。PHP为我们提供了这么一个函数:session_regenerate_id(),但是何时使用这个函数,是整个问题的关键。作为服务器与客户端之间的无缝转换,关键的一步在于dump的时候,偷偷的将新生成的sessId写入记录容器(数据库,内存or I/O),然而dump函数,是作为整个PHP运行过程中的末尾部分,header已经输出,你无法重新改写header。或者简单的说,我们日常编写的PHP代码,无论篇幅大小,都是经由open -> dump这个过程中执行的,当会话机制运行到dump过程时,我们已经无可扭转。


解决此问题的落实点在于最初执行session_start的时候,session最初启动的时候,即可执行session_regenerate_id,但是需要一个全局的监控(这时候js的让人十分怀念,可是php无法做到)。


 

$sessName = "MY_APP_SID";
$isRegenerateId = false;
session_name($sessName);
session_start(); // => 此处将立刻调用刚才托管的open和pick两个函数
if ($isRegenerateId)
session_regenerate_id();

 


而在open的函数中,则是用于通知$isRegenerateId是否变更了:


 

function open($sessId)
....

if ($sess->isValid()) { /* 检查会话是否有效 */
return $anyVal;
}
else {
$isRegenerateId = true;
}
....

 

 

 


dump如何处理,这里就不再例举了,因为当执行了regenerate以后,全局的session_id都会自动的跟随变化。


不过美中不足的是,PHP的OOP的闭合特性不够强大,$isRegenerateId这么一个重要的变量,暴露在任何环境中都是极度危险的,即便使用private也是十分危险的,越是私有,越容易让代码的状态不可控(这方面让人非常羡慕Scala,一个能同时拥有val和var,你只需要声明sealed类作为一个全局的监视器,就能轻松解决此问题,可惜PHP不行)。于是可以有以下方式进行调整:


 

$sessName = "MY_APP_SID";
session_name($sessName);
session_start(); // => 此处将立刻调用刚才托管的open和pick两个函数
if (defined('REGENERATE_SESS_ID'))
session_regenerate_id();

 


很自然的,open里面,当isValid为false时,define('REGENERATE_SESS_ID', true)即可。


最后说说,不进行regenerate的后果是什么:


首先,从会话托管的设计初衷而言,让每一个会话本身具有生命周期,就是为了避开沉重的全局回收,让session数据得以冗余而暂时存在,却又不会对该会话持有者造成太大的影响与干扰,如果强行删除会话,客户端必然需要重新登录,这也只是其中的一种情况,如果没有控制好,可能会导致同样的sessId存在多条实例,或者本应超过生命周期的会话重新被激活。当然,也许处在全局的安全性考虑,客户端的个别体验都可以忽略不计,尤其是网站应用方面而言,很少有持续维持会话生命超过十几个小时的。然而这也仅限于网站方面,对于API,或者游戏,对于会话生命周期的要求,就越发的严格。如果站在企业级应用,某些时刻,某些局部,真的是分秒必争。能做到无缝的session生命周期的传承与转换,还是十分重要的。


其次,作为一个系统而言,session往往持有着重大的用户信息,无论怎么完善的系统设计,客户端和服务器之间总需要一条红绳。在特殊应用中,session总是会持有更多特殊的数据,安全的转移,并制造适当的冗余,才能为日后的数据管理,数据回报提供更加完善准确的数据。再者,只有全面的实现设计的全部,才能总结并推演出更加完善的结构,不知道bug的存在,才是最大的风险。

posted @ 2010-06-21 23:45 Janpoem 阅读(54) 评论(0) 编辑

2010年2月6日

问题源自于深空博客的这篇文章《由会话重定向看到的对象销毁问题》,嗯,我以为这种问题早有人处理过了,因为2年前我就解决了此问题。解决办法已经发在phpchina.com的原创区:《关于PHP的Session处理的问题》。不过我在自己的博客上也发表同样的一篇帖子,留作备份。

 

在专家板块看到有人提出对Session处理机制的问题,原文《由会话重定向看到的对象销毁问题》。由于本人没有在专家板块发帖的资格,所以在这里发。

大概在08年年头我开始放弃Ruby on Rails转移到PHP开发,并以RoR的一些精神开发基于PHP的MVC框架,08年年底的时候,曾在phpchina这里发过一帖《自写MVC框架 Agi PHPMVC(核心)》,可以这么说,从我接触PHP以来一直是以自写的MVC框架在进行开发。目前该框架取名Agi on Rails,已经进入正式版的1.2版,下一个release版本将会考虑开源。该框架已经成功稳定的运行在多个Server Env(Windows、Linux,IIS、Apache、Lighttpd、Nginx),开发过超过20个项目,承受过一天超过1200万PV的洗礼(预计并发峰值在200左右)。为何加这一个插曲,是为了强调,我是坚持将数据操作写在Model层的,而Session处理的逻辑,是被设计成一个 Model,而随着众多Model被Controler和View层调用。而开发者,是可以针对Session这个模块进行后期的高级的逻辑封装的。

废话就不多说了,解决方案如下: 

 

1 // 数据库连接的抽象层
2  abstract class DB_Connector {
3
4 protected static
5 $_register = array();
6
7 static public function connect($anyKey) {
8 // 假设传入的$anyKey指定要使用MySQL进行连接
9 // 这中间的一些判断这里就忽略了
10   if (!isset(self::$_register[$anyKey])) {
11 self::$_register[$anyKey] = new DB_Connector_MySQL();
12 }
13 return self::$_register[$anyKey];
14 }
15
16 static public function disconnect($anyKey) {
17 self::connect($anyKey)->disconnect();
18 }
19
20 static public function handleDisconnect($anyKey) {
21 self::connect($anyKey)->handleDisconnect();
22 }
23 }
24
25  // 数据库连接的驱动层
26  class DB_Connector_MySQL {
27
28 protected
29 $_connector = null,
30 $_isHandleDisconnect = false;
31
32 public function __construct() {
33 // 执行具体的连接
34   $this->_connector = new MySQLDriver();
35 }
36
37 public function __destruct() {
38 if (!$this->_isHandleDisconnect)
39 $this->disconnect();
40 }
41
42 public function disconnect() {
43 $this->_connector = null;
44 }
45
46 public function handleDisconnect() {
47 $this->_isHandleDisconnect = true;
48 }
49 }
50
51  // Session的实现层
52 // Any_ActiveRecord是Model的抽象层,这里就不实现了
53  class Session extends Any_ActiveRecord {
54
55 protected static
56 $_connectorKey = 'Any';
57
58 // 标准实现
59 static public function open() {
60 // 一旦将Session处理转移给DB层面去控制
61 // 就意味着数据库连接的释放,也必须转交给这个Session模块来处理
62 DB_Connector::handleDisconnect(self::$_connectorKey);
63 // 其他启动配置,包括Session GC清理的基数等等
64 }
65
66 // 标准实现
67 //
68 static public function pick($sId) {
69
70 }
71
72 // 标准实现
73 static public function dump($sId, $val) {
74
75 }
76
77 // 标准实现
78 static public function destroy($sId) {
79
80 }
81
82 // 标准实现
83 static public function gc() {
84
85 }
86
87 // 标准实现
88 static public function close() {
89 // 一切OK,再由Session Close的时候,释放数据库连接
90 DB_Connector::disconnect(self::$_connectorKey);
91 }
92 }

至此,第一个问题解决了,就是关于数据库连接的释放问题。但是这里存在第二个问题(假如你在使用的框架,取出的Session是一个数组,或者你直接就取出的是一个数组,可以忽略第二个问题),就是按照常理,一个Session经由Model取出,理应被是一个Session的实例,然后,由于PHP本身的运行机制的问题,变量的释放,往往早于Session的注销。这时就要发挥出OO的本色了:

 

根据上述的Session类,我们进行一点点改造:

 

1 // Session的实现层
2 // Any_ActiveRecord是Model的抽象层,这里就不实现了
3 class Session extends Any_ActiveRecord {
4
5 protected static
6 $_connectorKey = 'Any',
7 $_currSess = null;
8
9 // 标准实现
10 static public function open() {
11 // 一旦将Session处理转移给DB层面去控制
12 // 就意味着数据库连接的释放,也必须转交给这个Session模块来处理
13 DB_Connector::handleDisconnect(self::$_connectorKey);
14 // 其他启动配置,包括Session GC清理的基数等等
15 }
16
17 // 标准实现
18 // 拿出Session
19 static public function pick($sId) {
20 self::$_currSess = self::find_by_sess_id($sId);
21 if (!self::$_currSess->isEmpty())
22 return self::$_currSess->value;
23 return false;
24 }
25
26 // 标准实现
27 static public function dump($sId, $val) {
28 // 新访客
29 if (self::$_currSess->isEmpty())
30 self::$_currSess->sess_id == $sId;
31 self::$_currSess->value = $val;
32 self::$_currSess->save();
33 }
34
35 // 标准实现
36 static public function destroy($sId) {
37
38 }
39
40 // 标准实现
41 static public function gc() {
42
43 }
44
45 // 标准实现
46 static public function close() {
47 // 一切OK,再由Session Close的时候,释放数据库连接
48 DB_Connector::disconnect(self::$_connectorKey);
49 self::$_currSess = null;
50 }
51 }

好了,大功告成!原理就不多说了,多做点测试吧。

 

将Session写成Model的好处是,可以有针对性的进行单元测试。也许有用户会担心,你把Session放在数据库层,能承受得多大的并发量呢?

OK,我可以给出一些实际数据,一个投票的程序,PHP和MySQL跑在同一台服务器(Server系统是Ubuntu Server以Lighttpd,已经通过压力测试优化过fastcgi线程数字)上,3天收集有效投票记录总数900万+(注意,有效投票是指限制ip的,每一票都要检查ip和该ip上一次投票的时间),Session使用Model操作,以MyISAM引擎存放在MySQL的表中,Session主键已经刷到8位数。最高峰一天PV 1200万。

 

另:我发现cnblogs的源代码极其以及十分之丑陋,无法让人家复制代码,提供附件下载

posted @ 2010-02-06 02:35 Janpoem 阅读(153) 评论(0) 编辑

2009年12月29日

Eclipse 3.5汉化包,这是官方的汉化包,但是分40个包下载,而且Eclipse的汉化包下载也改了,没法用软件一次性下载了,每次都要点40*3=120次连接,疯了,打个包。。。。

 

点击下载

posted @ 2009-12-29 19:55 Janpoem 阅读(1532) 评论(0) 编辑

2009年12月12日

原文:http://www.garfieldtech.com/blog/magic-benchmarks

The day is nearly upon us! Drupal 7 will open up developers to PHP 5 functionality when it is released next year. Already, there is talk of how, and if, to leverage PHP 5's object handling now that we don't need to deal with the weirdness of PHP 4's object model. Of course, because it's Drupal, our army of performance czars want to know just what the cost is for object handling, and especially advanced object magic like __get(), __call(), the ArrayAccess interface, and so forth.

So let's find out. :-)

Benchmarking methodology

The exact numbers in the following tests aren't particularly interesting. What's interesting is their relative value. All tests are run in a single script (available at the bottom of this post) on the following system:

Lenovo Thinkpad T61 on AC power
Intel Core2 Duo 2.2 GHz
2 GB RAM
Kubuntu 7.10 "Gutsy"
PHP 5.2.3

Because it's a fairly beefy system, all tests are run 2,000,000 times so that we have worthwile numbers to compare. All times listed below are in seconds. Of course, any such tests will vary a bit between runs, and even between two tests in the same script. We're looking for overall trends here, not exact numbers, but it's important to keep in mind that micro-benchmarks are an inexact science. Also keep in mind that I don't know the internals of the PHP engine well at all, so my analysis is based on logical extrapolation, not actual knowledge of the PHP engine itself.

All tests use the following benchmarking mechanism:

<?php
error_reporting
(E_ALL | E_STRICT);
define('ITERATIONS', 2000000);
...
$start = microtime(true);
for (
$i=0; $i < ITERATIONS; ++$i) {
 
// something here we're testing.
}
$stop = microtime(true);
echo
"Test name: " . ($stop - $start) . " seconds". PHP_EOL;
?>

Functions

For completeness, we'll test ordinary functions, too. First define a dummy function that we can call:

<?php
function foo($a) {
  return;
}
?>

And we'll call it 4 different ways:

<?php
foo
(1); $foo = 'foo';
$foo(1); call_user_func('foo', 1); call_user_func_array('foo', array(1));
?>

Results:

Literal function    1.218
Variable function 1.305
call_user_func() 2.734
call_user_func_array() 3.386

As others have noted before, call_user_func*() is extremely slow. Unfortunately, it's also the main way to do function-level polymorphism in PHP.

Methods

Moving on the object oriented-code, our main interest here, let's look at three different ways of calling methods: Directly, via __call(), and via __call() with a generic pass-through using call_user_func_array():

<?php
class TestCall {
  function
normal() { return; }
  function
__call($method, $args) { return; }
}
$t = new TestCall(); // ... class TestCallSub {
  function
normal() { return; }
  function
bar() { return; }
  function
__call($method, $args) {
    if (
$method == 'foo') {
      return
call_user_func_array(array($this, 'bar'), $args);
    }
    return;
  }
}
$s = new TestCallSub(); // Normal method
$t->normal(); // __call() overhead
$t->doesntExist(); // __call() overhead with generic dispatch
$s->foo();
?>

Results:

Native Method                1.095
Magic Method (__call()) 3.018
Magic Method (with sub-function) 7.226

It looks like __call() is indeed not a speed deamon, but not as slow as I previously thought. Rather, it is call_user_func_array() that was the real killer. Between the two of them, call_user_func_array() has more overhead than __call() does. Mixing them is a performance nightmare.

Properties

PHP 5 also includes some magic overrides for properties: __get() and __set(). Let's see how expensive those are.

<?php
class TestGetSet {
  public
$foo = 1;
  public function
__get($var) {
    return
$this->foo;
  }
  public function
__set($var, $val) {
   
$this->foo = $val;
  }
}
$t = new TestGetSet(); $t->foo;
$t->bar;
$t->foo = 1;
$t->bar = 1;
?>

Results:

Get Native Property        0.619
Get Magic Property (__get()) 2.066
Set Native Property 0.752
Set Magic Property (__set()) 2.623

Again, magic is expensive. Curiously, both __get() and __set() are just over 3x the cost of the native equivalent (3.3x and 3.4x, respectively) while __call() is only 2.7x the cost.

Arrays

Another nifty feature of PHP 5 is the ArrayAccess interface, part of the Standard PHP Library. What does that cost us?

<?php
class TestArrayAccess implements ArrayAccess {
  private
$properties = array();
  public
$foo = 1;
  function
__construct($array) {
   
$this->properties = $array;
  }
  function
offsetExists($offset) {
    return isset(
$this->properties[$offset]);
  }
  function
offsetGet($offset) {
    return
$this->properties[$offset];
  }
  function
offsetSet($offset, $value) {
   
$this->properties[$offset] = $value;
  }
  function
offsetUnset($offset) {
    unset(
$this->properties[$offset]);
  }
}
$a = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D');
$t = new TestArrayAccess($a); // Get array property
$a['b'];
// Set array property
$a['b'] = 'B';
// Get object property
$t->foo;
// Set object property
$t->foo = 1;
// Get ArrayAccess property
$t['b'];
// Set ArrayAccess property
$t['b'] = 'B';
?>

Results:

Get Array Property        0.473
Set Array Property 0.655
Get Object Property 0.598
Set Object Property 0.733
Get ArrayAccess Property 2.379
Set ArrayAccess Property 3.030

So we can determine 3 things here. One, setting a variable is a bit more expensive than reading it, but not enormously so. That's not surprising. Two, arrays are very slightly faster than objects for just reading a public property directly, but again not by much and probably not enough to worry about (especially when there are plenty of more expensive operations, as we are finding). Three, the ArrayAccess interface eats your CPU.

At first that seems surprising, but consider that each array access must first detect that it's using the extra language magic, then call a method, and in our case that method is not just trivially returning as it did in the earlier tests. It's doing an array lookup and returning an actual value. Still, a 4.6x-5x increase in time feels high. It's even a bit more expensive than __get() and __set().

Inheritance

What about simple inheritance? There are many ways to do polymorphism. So far we've determined that call_user_func_array() is a really lousy one from a performance perspective, and a wrapping function is going to cost an extra function call each time. What if we use inheritance for more traditional, "classic" polymorphism? Let's have a go.

<?php
class Base {
  public
$test = 1;
  public function
baseMethod() { return; }
  public function
overrideMe() { return; }
}
class
Child extends Base {
  public
$child = 1;
  public function
childMethod() { return; }
  public function
overrideMe() { return; }
}
$b = new Base();
$c = new Child(); $b->test;
$c->test;
$c->child;
$b->baseMethod();
$c->baseMethod();
$c->childMethod();
$c->overrideMe();
?>

Results:

Get Base Property        0.617
Get Base Property from Child 0.611
Get Child Property 0.625
Get Base Method 1.185
Get Base Method from Child 1.142
Get Child Method 1.141
Override Child Method 1.124

Finally, some good news! Not really surprising news, either. When all is said and done, inheritance is basically free as far as CPU cycles go. That should not come as a surprise. Properties and methods of an object are inherited at creation time, not call time, so once the object is created it doesn't really matter how it was created. At least from a performance perspective, then, inheritance is not a concern.

Composition

Of course, the blogosphere has been hopping recently about how inheritance is evil and inflexible and composition is so much better and more flexible. The catch, though, is that composition does incur a run-time cost in terms of extra method calls. Let's see what that cost is.

<?php
class Used {
  public function
myMethod() { return; }
}
class
User {
  protected
$used;
  public function
__construct() {
   
$this->used = new Used();
  }
  public function
myMethod() { return $this->used->myMethod(); }
}
$u = new User(); $u->myMethod();
?>

Results:

Get Composed Method    2.232

Wrapping a method via composition roughly doubles the performance cost, which is exactly what we'd expect from adding one more method call to the stack. No surprises here, either. Consider that the cost of composition. At least it's cheaper than call_user_func_array(). :-)

Iterators

The last test we'll make involves iterators. SPL includes a huge collection of iterators, but we're only going to look at two of them. We'll compare iterating over a native array with an object that uses an internal iterator, using the Iterator interface, and one using an external Iterator via IteratorAggregate and ArrayIterator.

<?php
class Internal implements Iterator {
  protected
$a;
  public function
__construct(array $a) {
   
$this->a = $a;
  }
  public function
current() {
    return
current($this->a);
  }
  public function
key() {
    return
key($this->a);
  }
  public function
next() {
    return
next($this->a);
  }
  public function
rewind() {
    return
reset($this->a);
  }
  public function
valid() {
    return (
current($this->a) !== FALSE);
  }
}
class
External implements IteratorAggregate {
  protected
$a;
  public function
__construct(array $a) {
   
$this->a = $a;
  }
  public function
getIterator() {
    return new
ArrayIterator($this->a);
  }
}
$a = array('A', 'B', 'C', 'D');
$internal = new Internal($a);
$external = new External($a);

foreach (

$a as $item);
foreach (
$internal as $item);
foreach (
$external as $item);
?>

Results:

Iterate array            1.67
Iterate internal iterator 22.87
Iterate external iterator 6.06

Oh dear god make it stop! A trivially-simple internal iterator has a performance hit of more than an order of magnitude over a native array. An external iterator is cheaper, but still not cheap.

Let's consider why that is, though. Using the Iterator interface, we're forcing PHP to call into user-space 2-3 times per iteration. (I'm not sure of the exact internals, but at minimum it would need to call next() and valid() each iteration, plus key() if we're requesting it.) That's three method calls per iteration, not counting the behind-the-scenes engine code to make the magic work. Maybe it's not so surprising then. The external iterator is faster here because we're using the ArrayIterator object, provided by SPL and implemented entirely in C. If we used a user-space external iterator, I would expect results similar to those for the internal iterator.

The moral of the story here is, as always, C is faster than PHP. The more you can do in C, the faster your code will be. (Hey, that rhymes!) If possible, use IteratorAggregate and ArrayIterator over an internal iterator. If that's not possible for some reason, say you're iterating over some external resource like a file handle or database result set, be aware that it's going to cost you.

Summary

So what have we learned? We've learned that there is no such thing as a free lunch, unless you're getting it from your parents. (Amazing how programming parallels real life, isn't it?) All of PHP 5's advanced object-oriented features have a cost, and sometimes that cost is non-trivial.

Does that mean we should avoid using them? Of course not! Magic methods, iterators, ArrayAccess, and the like make solving certain types of problems far easier and faster for the programmer. In many cases, throwing more CPU at the code is cheaper than writing more, clunkier, harder-to-maintain code. And if the advanced features are not used in critical sections of the program, you may not even notice the difference. These benchmarks should be used as guidelines only; moving your database server from the same computer to a dedicated database box will likely yield a bigger performance boost than expunging all traces of __get() from your code, and will almost certainly cost far less to do.

There's one other important observation that we haven't really mentioned. One of the big complaints about PHP 4's object model was that it was dog slow compared to procedural code. Well, whatever the truth to that it is no longer the case. Calling a function and calling a method is virtually identical in cost, at least under modern versions of PHP. Polymorphic code can eve be faster if using inheritance over composition or function-level composition (or god-forbid call_user_func_array()), although as always beware the inheritance trap. As with anything else, use wisely.

The raw data is available below, as is a graph of the results courtesy of OpenOffice.org Calc. I've left the internal iterator out of the graph as it would visually throw everything else off. The complete benchmark script used is available below as well.

Results (2 million iterations)
Operation Seconds
Literal function 1.218
Variable function 1.305
call_user_func() 2.734
call_user_func_array() 3.386
Native Method 1.095
Magic Method (__call()) 3.018
Magic Method (with sub-function) 7.226
Get Native Property 0.619
Get Magic Property (__get()) 2.066
Set Native Property 0.752
Set Magic Property (__set()) 2.623
Get Array Property 0.473
Set Array Property 0.655
Get Object Property 0.598
Set Object Property 0.733
Get ArrayAccess Property 2.379
Set ArrayAccess Property 3.030
Get Base Property 0.617
Get Base Property from Child 0.611
Get Child Property 0.625
Get Base Method 1.185
Get Base Method from Child 1.142
Get Child Method 1.141
Override Child Method 1.124
Get Composed Method 2.232
Iterate array 1.67
Iterate internal iterator 22.87
Iterate external iterator 6.06

Benchmarks

http://www.garfieldtech.com/blog/magic-benchmarkshttp://www.garfieldtech.com/blog/magic-benchmarks
posted @ 2009-12-12 16:35 Janpoem 阅读(78) 评论(0) 编辑

原文:http://www.qianduan.net/rediscovering-html-tables.html

根 据我最近的一些实践以及在和一些读者进行关于HTML表格的使用问题沟通之后,决定写这篇文章。总的来说,我注意到由于误导性信息,他们对于 table的使用有种先入为主的厌恶。事实上很多人会说”我看到永远不应该使用表格”的说法,但是这绝对是错误的!这个建议只是针对使用HTML表格来定 义网页的布局,但是表格在方便的排列数据信息行和列方面非常完美,而且如果你一定要在一个页面上显示表列数据,你就不得不使用它们!为什么不呢?然而,在 这种情况下,一些人无视了用于table的某些HTML标签的存在并且不知道该如何正确的使用它们。\

HTML有10个表格相关标签。下面是一个带有简介的列表,但是首先,文档要被正确的定义在HTML 4.01/XHTML 1或HTML 5下面:

一个基本的表格结构如下:

它包含一个标题、头部、主体和底部。正确的HTML元素顺序是:

  1. <table>
  2. <caption>
  3. <thead>
  4. <tfoot>
  5. <tbody>

你也可以使用<col> 和<colgroup> 来定义表格的列或为列分组:

  1. <table>
  2. <caption>
  3. <colgroup>
  4. <col>
  5. <thead>
  6. <tfoot>
  7. <tbody>

下面是一个正确的表格结构实例:

<table border="1">
 <caption>Table caption here</caption>
 <colgroup span="1" style="background:#DEDEDE;"/>
 <colgroup span="2" style="background:#EFEFEF;"/>
 
 <!-- Table Header-->
 <thead>
 <tr>
  <th>Head 1</th>
   <th>Head 2</th>
   <th>Head 3</th>
  </tr>
 </thead>
 
 <!-- Table Footer-->
 <tfoot>
  <tr>
   <td>Foot 1</td>
   <td>Foot 2</td>
   <td>Foot 3</td>
  </tr>
 </tfoot>
 
 <!-- Table Body-->
 <tbody>
  <tr>
   <td>A</td>
   <td>B</td>
   <td>C</td>
  </tr>
  <tr>
   <td>D</td>
   <td>E</td>
   <td>F</td>
  </tr>
 </tbody>
</table>

在浏览器中的结果如下图所示:

关于表格的一些技巧

关于table的更多详细内容可以查看W3C的文档: w3 Introduction to tables

最后留一个非常简单的问题给大家,CSS的哪个属性等效于table的cellpadding属性?

posted @ 2009-12-12 16:32 Janpoem 阅读(23) 评论(0) 编辑

2009年12月11日

原文作者:IIduce

原文地址:http://css9.net/css-font-face-solution/

作者博客:http://css9.net/


字体使用是网页设计中不可或缺的一部分。经常地,我们希望在网页中使用某一特定字体,但是该字体并非主流操作系统的内置字体,这样用户在浏览页面的时候就有可能看不到真实的设计。美工设计师最常做的办法是把想要的文字做成图片,这样做有几个明显缺陷:1. 不可能大范围的使用该字体;2. 图片内容相对使用文字不易修改;3. 不利于网站SEO(主流搜索引擎不会将图片alt内容作为判断网页内容相关性的有效因素)。网络上有一些使用sIFR技术、或javascript/flash hack的方法,但实现起来或繁琐,或有缺陷。下面要讲的是如何只通过CSS的@font-face属性来实现在网页中嵌入任意字体。

 

【第一步】

 

获取要使用字体的三种文件格式,确保能在主流浏览器中都能正常显示该字体。

下面要解决的是如何获取到某种字体的这三种格式文件。一般地,我们在手头上(或在设计资源站点已经找到)有该字体的某种格式文件,最常见的是.TTF文件,我们需要通过这种文件格式转换为其余两种文件格式。字体文件格式的转换可以通过网站FontsQuirrelonlinefontconverter提供的在线字体转换服务获取。这里推荐第一个站点,它允许我们选择需要的字符生成字体文件(在服务的最后一个选项),这样就大大缩减了字体文件的大小,使得本方案更具实用性。

 

【第二步】

 

获取到三种格式的字体文件后,下一步要在样式表中声明该字体,并在需要的地方使用该字体。

字体声明如下:

 

 

 1 @font-face {
 2     font-family: 'fontNameRegular';
 3     src: url('fontName.eot');
 4     src: local('fontName Regular'), 
 5          local('fontName'), 
 6          url('fontName.woff') format('woff'), 
 7          url('fontName.ttf') format('truetype'), 
 8          url('fontName.svg#fontName') format('svg');
 9 }  
10 /*其中fontName替换为你的字体名称*/


在页面中需要的地方使用该字体:

1 p  { font: 13px fontNameRegular, Arial, sans-serif; }
2 h1 {font-family: fontNameRegular}


或者

 

1 <p style="font-family: fontNameRegular">掬水月在手,落花香满衣</p>


示例就不转载了,欢迎去作者的原文看看效果。

 

posted @ 2009-12-11 09:03 Janpoem 阅读(18) 评论(0) 编辑

2009年11月17日

摘要: 国外著名网站smashingmagazine给大家带来了50款优秀而又实用的PHP工具,其中包括PHP代码测试、优化工具,图像处理工具,以及开发过程必须的软件配置管理工具等,相信这些工具能够让使用PHP的朋友如虎添翼! 50款优秀而又实用的PHP工具:一、PHP代码调试工具Webgrind Webgrind完美支持PHP 5,安装迅速简便,可以跨越多个平台运行。 Xdebug Xdebug是使用极...阅读全文
posted @ 2009-11-17 22:57 Janpoem 阅读(120) 评论(0) 编辑

2009年8月2日

    该文被密码保护。
posted @ 2009-08-02 16:20 Janpoem 阅读(14) 评论(0) 编辑

2009年8月1日

摘要: 原文:http://hi.baidu.com/dosmark/blog/item/d4528a8b2cf1ead4fc1f10fb.html 安装最新的 JDK 必须安装 Java SE Development Kit (JDK) 6 Update 10 或更新的版本,早期版本对字体反锯齿支持不好。 下载地址:http://java.sun.com/javase/downloads/index....阅读全文
posted @ 2009-08-01 22:53 Janpoem 阅读(255) 评论(0) 编辑
摘要: 第 1 部分: 使用操作码缓存软件提升性能和吞吐量原文:http://www.ibm.com/developerworks/cn/opensource/os-php-fastapps1/PHP 是一种脚本语言,常用于创建 Web 应用程序。它易于掌握,并能迅速生成可视化结果。然而,由于 PHP 是以解释的方式执行的,所以 PHP 代码每次执行时都会被解析和转换成操作码(opcode)。操作码缓存...阅读全文
posted @ 2009-08-01 02:55 Janpoem 阅读(132) 评论(0) 编辑
仅列出标题  下一页

导航

公告

统计