Commit 5f0198a4 authored by jepso's avatar jepso

New documentation tool.

parent 813886a0
# RewriteEngine on
# RewriteRule ^chapter/(.*)/(.*)$ index.php [r=404]
# RewriteRule ^chapter/(.*)$ index.php?chapter=$1
# RewriteRule ^one-page$ index.php?one-page=1
<?php
require_once("highlight.php");
require_once('Text/Wiki.php');
class DocTool
{
private $_wiki;
private $_toc;
private $_options = array('max-level' => 1,
'lang' => 'en',
'default-lang' => 'en',
'one-page' => false,
'section' => null,
'clean-url' => false,
'base-url' => '');
private $_lang = array();
public function __construct($filename)
{
$this->_wiki = new Text_Wiki();
$this->_wiki->disableRule('Wikilink');
$this->_toc = new Sensei_Doc_Toc($filename);
}
public function getOption($option)
{
return $this->_options[$option];
}
public function setOption($option, $value)
{
switch ($option) {
case 'max-level':
if (!is_int($value)) {
throw new Exception('Value must be an integer.');
}
break;
case 'one-page':
case 'clean-url':
if (!is_bool($value)) {
throw new Exception('Value must be a boolean.');
}
break;
case 'locale':
case 'base-url':
if (!is_string($value)) {
throw new Exception('Value must be a string.');
}
break;
case 'section':
if (! $value instanceof Sensei_Doc_Section) {
throw new Exception('Value must be an instance of Sensei_Doc_Section.');
}
break;
default:
throw new Exception('Unknown option.');
}
$this->_options[$option] = $value;
}
public function addLanguage(array $translations, $lang)
{
$this->_lang[$lang] = $translations;
}
public function translate($string)
{
$language = $this->getOption('lang');
if (array_key_exists($language, $this->_lang)
&& array_key_exists($string, $this->_lang[language])) {
return $this->_lang[$language][$string];
} else {
return $string;
}
}
public function renderToc($toc = null)
{
if (!$toc) {
$toc = $this->_toc;
}
$classes = array();
if ($toc instanceof Sensei_Doc_Toc) {
$class = '';
if ($this->getOption('one-page')) {
$class = ' class="one-page"';
}
echo '<div' . $class . ' id="table-of-contents">' . "\n";
echo '<h1>Table of Contents</h1>' . "\n";
$classes[] = 'tree';
} else {
$isParent = false;
$section = $this->getOption('section');
if ($section !== null) {
$current = $section;
do {
if ($current === $toc) {
$isParent = true;
break;
}
} while (($current = $current->getParent()) !== null);
}
if (! $isParent) {
$classes[] = 'closed';
}
}
$classes = implode(' ', $classes);
if ($classes === '') {
echo "<ul>\n";
} else {
echo "<ul class=\"$classes\">\n";
}
for ($i = 0; $i < $toc->count(); $i++) {
$child = $toc->getChild($i);
if ($child === $this->getOption('section')) {
echo '<li class="current">';
} else {
echo '<li>';
}
echo '<a href="' . $this->makeUrl($child->getPath()) . '">';
echo $child->getIndex() . ' ' . $child->getName() . '</a>';
if ($child->count() > 0) {
echo "\n";
$this->renderToc($child);
}
echo '</li>' . "\n";
}
echo '</ul>' . "\n";
if ($toc instanceof Sensei_Doc_Toc) {
echo '</div>' . "\n";
}
}
public function makeUrl($path)
{
$prefix = $this->getOption('base-url');
if (!$this->getOption('one-page')) {
if ($this->getOption('clean-url')) {
$prefix .= 'chapter/';
} else {
$prefix .= '?chapter=';
}
}
$parts = explode(':', $path);
$firstPath = array_slice($parts, 0, $this->getOption('max-level'));
$href = $prefix . implode(':', $firstPath);
$anchorName = $this->makeAnchor($path);
if (!empty($anchorName)) {
$href .= '#' . $anchorName;
}
return $href;
}
public function makeAnchor($path)
{
$pathParts = explode(':', $path);
$anchorParts = array_slice($pathParts, $this->getOption('max-level'));
$anchorName = implode(':', $anchorParts);
return $anchorName;
}
public function render()
{
if ($this->getOption('one-page')) {
for ($i = 0; $i < count($this->_toc); $i++) {
$this->renderSection($this->_toc->getChild($i));
}
} else {
$section = $this->getOption('section');
if (!$section) {
throw new Exception('Section has not been set.');
} else {
$this->renderSection($section);
}
}
}
protected function renderSection($section)
{
$level = $section->getLevel();
$name = $section->getName();
$index = $section->getIndex();
if ($section->getLevel() == 1) {
echo '<div class="chapter">' . "\n";
echo "<h$level>Chapter $index ";
} else {
echo '<div class="section">' . "\n";
echo "<h$level>$index ";
}
if ($section->getLevel() > $this->getOption('max-level')) {
echo '<a id="' . $this->makeAnchor($section->getPath()) . '">';
echo $name;
echo '</a>';
} else {
echo $name;
}
echo "</h$level>\n";
if ($level === 1 && !$this->getOption('one-page')) {
//$this->renderToc($this->_toc);
}
echo $this->_wiki->transform($section->getText());
for ($i = 0; $i < count($section); $i++) {
$this->renderSection($section->getChild($i));
}
echo '</div>' . "\n";
}
public function findByPath($path)
{
return $this->_toc->findByPath($path);
}
public function findByIndex($index)
{
return $this->_toc->findByIndex($index);
}
}
\ No newline at end of file
Doctrine allows you to bind connections to components (= your ActiveRecord classes). This means everytime a component issues a query or data is being fetched from the table the component is pointing at Doctrine will use the bound connection.
<code type="php">
$conn = $manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
$conn2 = $manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
$manager->bindComponent('User', 'connection 1');
$manager->bindComponent('Group', 'connection 2');
$q = new Doctrine_Query();
// Doctrine uses 'connection 1' for fetching here
$users = $q->from('User u')->where('u.id IN (1,2,3)')->execute();
// Doctrine uses 'connection 2' for fetching here
$groups = $q->from('Group g')->where('g.id IN (1,2,3)')->execute();
</code>
In order to connect to a database through Doctrine, you have to create a valid DSN - data source name.
Doctrine supports both PEAR DB/MDB2 like data source names as well as PDO style data source names. The following section deals with PEAR like data source names. If you need more info about the PDO-style data source names see http://www.php.net/manual/en/function.PDO-construct.php.
The DSN consists in the following parts:
**phptype**: Database backend used in PHP (i.e. mysql , pgsql etc.)
**dbsyntax**: Database used with regards to SQL syntax etc.
**protocol**: Communication protocol to use ( i.e. tcp, unix etc.)
**hostspec**: Host specification (hostname[:port])
**database**: Database to use on the DBMS server
**username**: User name for login
**password**: Password for login
**proto_opts**: Maybe used with protocol
**option**: Additional connection options in URI query string format. options get separated by &. The Following table shows a non complete list of options:
**List of options**
||~ Name ||~ Description ||
|| charset || Some backends support setting the client charset.||
|| new_link || Some RDBMS do not create new connections when connecting to the same host multiple times. This option will attempt to force a new connection. ||
The DSN can either be provided as an associative array or as a string. The string format of the supplied DSN is in its fullest form:
`` phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value ``
Most variations are allowed:
phptype://username:password@protocol+hostspec:110//usr/db_file.db
phptype://username:password@hostspec/database
phptype://username:password@hostspec
phptype://username@hostspec
phptype://hostspec/database
phptype://hostspec
phptype:///database
phptype:///database?option=value&anotheroption=anothervalue
phptype(dbsyntax)
phptype
The currently supported database backends are:
||//fbsql//|| -> FrontBase ||
||//ibase//|| -> InterBase / Firebird (requires PHP 5) ||
||//mssql//|| -> Microsoft SQL Server (NOT for Sybase. Compile PHP --with-mssql) ||
||//mysql//|| -> MySQL ||
||//mysqli//|| -> MySQL (supports new authentication protocol) (requires PHP 5) ||
||//oci8 //|| -> Oracle 7/8/9/10 ||
||//pgsql//|| -> PostgreSQL ||
||//querysim//|| -> QuerySim ||
||//sqlite//|| -> SQLite 2 ||
A second DSN format is supported phptype(syntax)://user:pass@protocol(proto_opts)/database
If your database, option values, username or password contain characters used to delineate DSN parts, you can escape them via URI hex encodings:
``: = %3a``
``/ = %2f``
``@ = %40``
``+ = %2b``
``( = %28``
``) = %29``
``? = %3f``
``= = %3d``
``& = %26``
Warning
Please note, that some features may be not supported by all database backends.
Example
**Example 1.** Connect to database through a socket
mysql://user@unix(/path/to/socket)/pear
**Example 2.** Connect to database on a non standard port
pgsql://user:pass@tcp(localhost:5555)/pear
**Example 3.** Connect to SQLite on a Unix machine using options
sqlite:////full/unix/path/to/file.db?mode=0666
**Example 4.** Connect to SQLite on a Windows machine using options
sqlite:///c:/full/windows/path/to/file.db?mode=0666
**Example 5.** Connect to MySQLi using SSL
mysqli://user:pass@localhost/pear?key=client-key.pem&cert=client-cert.pem
Lazy-connecting to database is handled via Doctrine_Db wrapper. When using Doctrine_Db instead of PDO / Doctrine_Adapter, lazy-connecting to database is being performed (that means Doctrine will only connect to database when needed).
This feature can be very useful when using for example page caching, hence not actually needing a database connection on every request. Remember connecting to database is an expensive operation.
<code type="php">
// we may use PDO / PEAR like DSN
// here we use PEAR like DSN
$dbh = new Doctrine_Db('mysql://username:password@localhost/test');
// !! no actual database connection yet !!
// initalize a new Doctrine_Connection
$conn = Doctrine_Manager::connection($dbh);
// !! no actual database connection yet !!
// connects database and performs a query
$conn->query('FROM User u');
</code>
From the start Doctrine has been designed to work with multiple connections. Unless separately specified Doctrine always uses the current connection for executing the queries. The following example uses {{openConnection()}} second argument as an optional connection alias.
<code type="php">
// Doctrine_Manager controls all the connections
$manager = Doctrine_Manager::getInstance();
// open first connection
$conn = $manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
</code>
For convenience {{Doctrine_Manager}} provides static method {{connection()}} which opens new connection when arguments are given to it and returns the current connection when no arguments have been speficied.
<code type="php">
// open first connection
$conn = Doctrine_Manager::connection(new PDO('dsn','username','password'), 'connection 1');
$conn2 = Doctrine_Manager::connection();
// $conn2 == $conn
</code>
The current connection is the lastly opened connection.
<code type="php">
// open second connection
$conn2 = $manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
$manager->getCurrentConnection(); // $conn2
</code>
You can change the current connection by calling {{setCurrentConnection()}}.
<code type="php">
$manager->setCurrentConnection('connection 1');
$manager->getCurrentConnection(); // $conn
</code>
You can iterate over the opened connection by simple passing the manager object to foreach clause. This is possible since {{Doctrine_Manager}} implements special {{IteratorAggregate}} interface.
<code type="php">
// iterating through connections
foreach($manager as $conn) {
}
</code>
Opening a new database connection in Doctrine is very easy. If you wish to use PDO (www.php.net/PDO) you can just initalize a new PDO object:
<code type="php">
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
$dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
</code>
If your database extension isn't supported by PDO you can use special Doctrine_Adapter class (if availible). The following example uses db2 adapter:
<code type="php">
$dsn = 'db2:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
$dbh = Doctrine_Adapter::connect($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
</code>
The next step is opening a new Doctrine_Connection.
<code type="php">
$conn = Doctrine_Manager::connection($dbh);
</code>
Doctrine is quite big framework and usually dozens of files are being included on each request. This brings a lot of overhead. In fact these file operations are as time consuming as sending multiple queries to database server. The clean separation of class per file works well in developing environment, however when project goes commercial distribution the speed overcomes the clean separation of class per file -convention.
Doctrine offers method called compile() to solve this issue. The compile method makes a single file of most used Doctrine components which can then be included on top of your script. By default the file is created into Doctrine root by the name Doctrine.compiled.php.
Compiling is a method for making a single file of most used doctrine runtime components including the compiled file instead of multiple files (in worst cases dozens of files) can improve performance by an order of magnitude. In cases where this might fail, a Doctrine_Exception is throw detailing the error.
<code type='php'>
Doctrine::compile();
// on some other script:
require_once('path_to_doctrine/Doctrine.compiled.php');
</code>
+++ Introduction
+++ Export options
<code type='php'>
// export everything, table definitions and constraints
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL);
// export classes without constraints
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_TABLES ^
Doctrine::EXPORT_CONSTRAINTS);
// turn off exporting
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_NONE);
</code>
The installation of doctrine is very easy. Just get the latest revision of Doctrine from http://doctrine.pengus.net/svn/trunk.
You need a SVN(Subversion) client for downloading Doctrine.
In order to check out Doctrine in the current directory using the **svn** command line tool use the following code:
<code type="bash">
svn co http://doctrine.pengus.net/svn/trunk .
</code>
If you do not have a SVN client, chose one from the list below. Find the **Checkout** option and enter http://doctrine.pengus.net/svn/trunk in the **path** or **repository url** parameter. There is no need for a username or password to check out Doctrine.
* [http://tortoisesvn.tigris.org/ TortoiseSVN] a Windows application that integrates into Windows Explorer
* [http://www.apple.com/downloads/macosx/development_tools/svnx.html svnx] a Mac OS X GUI svn application
* Eclipse has SVN integration through the [http://subclipse.tigris.org/ subeclipse] plugin
+++ Include and autoload
In order to use Doctrine in your project it must first be included.
<code type='php'>
require_once('path-to-doctrine/lib/Doctrine.php');
</code>
Doctrine support [http://www.php.net/autoload Autoloading] for including files so that you do not have to include anything more then the base file. There are two different strategies that can be used to do this:
If you do use the **__autoload** function for your own logic you can use it.
<code type='php'>
function __autoload($class) {
Doctrine::autoload($class);
}
</code>
If your project uses autoload and/or you have other libraries that use it you could use [http://www.php.net/manual/en/function.spl-autoload-register.php spl_autoload_register] to register more then one autoloading function.
<code type="php">
spl_autoload_register(array('Doctrine', 'autoload'));
</code>
Doctrine requires PHP >= 5.1. it doesn't require any external libraries. For database abstraction Doctrine uses PDO which is bundled with php by default. Doctrine also requires a little adodb-hack for table creation, which comes with doctrine.
Doctrine_Record is the basic component of every doctrine-based project. There should be atleast one Doctrine_Record for each of your database tables. Doctrine_Record follows the [http://www.martinfowler.com/eaaCatalog/activeRecord.html Active Record pattern]
Doctrine auto-creates database tables and always adds a primary key column named 'id' to tables that doesn't have any primary keys specified. Only thing you need to for creating database tables is defining a class which extends Doctrine_Record and setting a setTableDefinition method with hasColumn() method calls.
An short example:
We want to create a database table called 'user' with columns id(primary key), name, username, password and created. Provided that you have already installed Doctrine these few lines of code are all you need:
<code type='php'>
require_once('lib/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
class User extends Doctrine_Record {
public function setTableDefinition() {
// set 'user' table columns, note that
// id column is always auto-created
$this->hasColumn('name','string',30);
$this->hasColumn('username','string',20);
$this->hasColumn('password','string',16);
$this->hasColumn('created','integer',11);
}
}
</code>
We now have a user model that supports basic CRUD opperations!
+++ Introduction
A common case when looking for ORM tools like Doctrine is that the database and the code that access it is growing large/complex. A more substantial tool is needed then manual SQL code.
Doctrine has support for generating Doctrine_Record classes from your existing database. There is no need for you to manually write all the Doctrine_Record classes for your domain model.
+++ Making the first import
Let's consider we have a mysql database called test with a single table called 'file'.
The file table has been created with the following sql statement:
<code type="sql">
CREATE TABLE file (
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(150),
size BIGINT,
modified BIGINT,
type VARCHAR(10),
content TEXT,
path TEXT,
PRIMARY KEY(id))
</code>
Now we would like to convert it into Doctrine record class. It can be achieved easily with the following code snippet:
<code type='php'>
require_once('lib/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
$conn = Doctrine_Manager::connection(new Doctrine_Db('mysql://root:dc34@localhost/test'));
// import method takes one parameter: the import directory (the directory where
// the generated record files will be put in
$conn->import->import('myrecords');
</code>
That's it! Now there should be a file called File.php in your myrecords directory. The file should look like:
<code type='php'>
/**
* This class has been auto-generated by the Doctrine ORM Framework
* Created: Saturday 10th of February 2007 01:03:15 PM
*/
class File extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('id', 'integer', 4, array('notnull' => true,
'primary' => true,
'unsigned' > true,
'autoincrement' => true));
$this->hasColumn('name', 'string', 150);
$this->hasColumn('size', 'integer', 8);
$this->hasColumn('modified', 'integer', 8);
$this->hasColumn('type', 'string', 10);
$this->hasColumn('content', 'string', null);
$this->hasColumn('path', 'string', null);
}
public function setUp()
{
}
}
</code>
+++ Import options
+++ Column naming
+++ Column aliases
+++ Default values
+++ Data types
+++ About type conversion
Doctrine offers a way of setting column aliases. This can be very useful when you want to keep the application logic separate from the
database logic. For example if you want to change the name of the database field all you need to change at your application is the column definition.
<code type="php">
class Book extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('bookName as name', 'string');
}
}
$book = new Book();
$book->name = 'Some book';
$book->save();
</code>
One problem with database compatibility is that many databases differ in their behaviour of how the result set of a query is returned. MySQL leaves the field names unchanged, which means if you issue a query of the form "SELECT myField FROM ..." then the result set will contain the field 'myField'.
Unfortunately, this is just the way MySql and some other databases do it. Postgres for example returns all field names in lowercase whilst Oracle returns all field names in uppercase. "So what? In what way does this influence me when using Doctrine?", you may ask. Fortunately, you don't have to bother about that issue at all.
Doctrine takes care of this problem transparently. That means if you define a derived Record class and define a field called 'myField' you will always access it through $record->myField (or $record['myField'], whatever you prefer) no matter whether you're using MySQL or Postgres or Oracle etc.
In short: You can name your fields however you want, using under_scores, camelCase or whatever you prefer.
Doctrine supports default values for all data types. When default value is attached to a record column this means two of things.
First this value is attached to every newly created Record.
<code type="php">
class User extends Doctrine_record {
public function setTableDefinition() {
$this->hasColumn('name', 'string', 50, array('default' => 'default name'));
}
}
$user = new User();
print $user->name; // default name
</code>
Also when exporting record class to database DEFAULT //value// is attached to column definition statement.
+++ Introduction
//From [http://www.postgresql.org/docs/8.2/static/ddl-constraints.html PostgreSQL Documentation]://
> Data types are a way to limit the kind of data that can be stored in a table. For many applications, however, the constraint they provide is too coarse. For example, a column containing a product price should probably only accept positive values. But there is no standard data type that accepts only positive numbers. Another issue is that you might want to constrain column data with respect to other columns or rows. For example, in a table containing product information, there should be only one row for each product number.
Doctrine allows you to define *portable* constraints on columns and tables. Constraints give you as much control over the data in your tables as you wish. If a user attempts to store data in a column that would violate a constraint, an error is raised. This applies even if the value came from the default value definition.
Doctrine constraints act as database level constraints as well as application level validators. This means double security: the database doesn't allow wrong kind of values and neither does the application.
+++ Notnull
A not-null constraint simply specifies that a column must not assume the null value. A not-null constraint is always written as a column constraint.
The following definition uses a notnull constraint for column {{name}}. This means that the specified column doesn't accept null values.
<code type='php'>
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 200, array('notnull' => true,
'primary' => true));
}
}
</code>
When this class gets exported to database the following SQL statement would get executed (in MySQL):
<code type="sql">
CREATE TABLE user (name VARCHAR(200) NOT NULL, PRIMARY KEY(name))
</code>
The notnull constraint also acts as an application level validator. This means that if Doctrine validators are turned on, Doctrine will automatically check that specified columns do not contain null values when saved.
If those columns happen to contain null values {{Doctrine_Validator_Exception}} is raised.
+++ Unique
Unique constraints ensure that the data contained in a column or a group of columns is unique with respect to all the rows in the table.
In general, a unique constraint is violated when there are two or more rows in the table where the values of all of the columns included in the constraint are equal. However, two null values are not considered equal in this comparison. That means even in the presence of a unique constraint it is possible to store duplicate rows that contain a null value in at least one of the constrained columns. This behavior conforms to the SQL standard, but some databases do not follow this rule. So be careful when developing applications that are intended to be portable.
The following definition uses a unique constraint for column {{name}}.
<code type='php'>
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 200, array('unique' => true));
}
}
</code>
>> Note: You should only use unique constraints for other than primary key columns. Primary key columns are always unique.
The following definition adds a unique constraint for columns {{name}} and {{age}}.
<code type='php'>
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 200);
$this->hasColumn('age', 'integer', 2);
$this->unique(array('name', 'age'));
}
}
</code>
+++ Check
Doctrine check constraints act as database level constraints as well as application level validators. When a record with check validators is exported additional CHECK constraints are being added to CREATE TABLE statement.
Doctrine provides the following simple check operators:
||~ Operator ||~ Description ||
|| {{gt}} || greater than constraint ( > ) ||
|| {{lt}} || less than constraint ( < ) ||
|| {{gte}} || greater than or equal to constraint ( >= ) ||
|| {{lte}} || less than or equal to constraint ( <= ) ||
Consider the following example:
<code type='php'>
class Product extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('id', 'integer', 4, 'primary');
$this->hasColumn('price', 'decimal', 18, array('gt' => 0);
}
}
</code>
When exported the given class definition would execute the following statement (in pgsql):
<code type="sql">
CREATE TABLE product (
id INTEGER,
price NUMERIC CHECK (price > 0)
PRIMARY KEY(id))
</code>
So Doctrine optionally ensures even at the database level that the price of any product cannot be below zero.
> NOTE: some databases don't support CHECK constraints. When this is the case Doctrine simple skips the creation of check constraints.
If the Doctrine validators are turned on the given definition would also ensure that when a record is being saved its price is always greater than zero.
If some of the prices of the saved products within a transaction is below zero, Doctrine throws Doctrine_Validator_Exception and automatically rolls back the transaction.
Doctrine_Record::hasColumn() takes 4 arguments:
# **column name**
# **column type**
# **column length**
# **column constraints and validators**
<code type='php'>
class Email extends Doctrine_Record {
public function setTableDefinition() {
// setting custom table name:
$this->setTableName('emails');
$this->hasColumn('address', // name of the column
'string', // column type
'200', // column length
array('notblank' => true,
'email' => true // validators / constraints
)
);
$this->hasColumn('address2', // name of the column
'string', // column type
'200', // column length
// validators / constraints without arguments can be
// specified also as as string with | separator
'notblank|email'
);
// Doctrine even supports the following format for
// validators / constraints which have no arguments:
$this->hasColumn('address3', // name of the column
'string', // column type
'200', // column length
array('notblank', 'email')
);
}
}
</code>
Doctrine automatically creates table names from the record class names. For this reason, it is recommended to name your record classes using the following rules:
* Use {{CamelCase}} naming
* Underscores are allowed
* The first letter must be capitalized
* The class name cannot be one of the following (these keywords are reserved in DQL API):
* {{ALL}}, {{AND}}, {{ANY}}, {{AS}}, {{ASC}}, {{AVG}}, {{BETWEEN}}, {{BIT_LENGTH}}, {{BY}}, {{CHARACTER_LENGTH}}, {{CHAR_LENGTH}}, {{COUNT}}, {{CURRENT_DATE}}, {{CURRENT_TIME}}, {{CURRENT_TIMESTAMP}}, {{DELETE}}, {{DESC}}, {{DISTINCT}}, {{EMPTY}}, {{EXISTS}}, {{FALSE}}, {{FETCH}}, {{FROM}}, {{GROUP}}, {{HAVING}}, {{IN}}, {{INNER}}, {{IS}}, {{JOIN}}, {{LEFT}}, {{LIKE}}, {{LOWER}}, {{MAX}}, {{MEMBER}}, {{MIN}}, {{MOD}}, {{NEW}}, {{NOT}}, {{NULL}}, {{OBJECT}}, {{OF}}, {{OR}}, {{ORDER}}, {{OUTER}}, {{POSITION}}, {{SELECT}}, {{SOME}}, {{SUM}}, {{TRIM}}, {{TRUE}}, {{UNKNOWN}}, {{UPDATE}}, {{UPPER}} and {{WHERE}}.
**Example:** {{My_PerfectClass}}
If you need to use a different naming schema, you can override this using the {{setTableName()}} method in the {{setTableDefinition()}} method.
Doctrine offers various table options. All table options can be set via {{Doctrine_Record::option($optionName, $value)}}.
For example if you are using MySQL and want to use INNODB tables it can be done as follows:
<code type="php">
class MyInnoDbRecord extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string');
$this->option('type', 'INNODB');
}
}
</code>
In the following example we set the collate and character set options:
<code type="php">
class MyCustomOptionRecord extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string');
$this->option('collate', 'utf8_unicode_ci');
$this->option('charset', 'utf8');
}
}
</code>
<code type="php">
class Forum_Category extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("root_category_id", "integer", 10);
$this->hasColumn("parent_category_id", "integer", 10);
$this->hasColumn("name", "string", 50);
$this->hasColumn("description", "string", 99999);
}
public function setUp() {
$this->hasMany("Forum_Category as Subcategory", "Subcategory.parent_category_id");
$this->hasOne("Forum_Category as Rootcategory", "Forum_Category.root_category_id");
}
}
class Forum_Board extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("category_id", "integer", 10);
$this->hasColumn("name", "string", 100);
$this->hasColumn("description", "string", 5000);
}
public function setUp() {
$this->hasOne("Forum_Category as Category", "Forum_Board.category_id");
$this->ownsMany("Forum_Thread as Threads", "Forum_Thread.board_id");
}
}
class Forum_Entry extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("author", "string", 50);
$this->hasColumn("topic", "string", 100);
$this->hasColumn("message", "string", 99999);
$this->hasColumn("parent_entry_id", "integer", 10);
$this->hasColumn("thread_id", "integer", 10);
$this->hasColumn("date", "integer", 10);
}
public function setUp() {
$this->hasOne("Forum_Entry as Parent", "Forum_Entry.parent_entry_id");
$this->hasOne("Forum_Thread as Thread", "Forum_Entry.thread_id");
}
}
class Forum_Thread extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("board_id", "integer", 10);
$this->hasColumn("updated", "integer", 10);
$this->hasColumn("closed", "integer", 1);
}
public function setUp() {
$this->hasOne("Forum_Board as Board", "Forum_Thread.board_id");
$this->ownsMany("Forum_Entry as Entries", "Forum_Entry.thread_id");
}
}
</code>
In the following example we make a user management system where
# Each user and group are entities
# User is an entity of type 0
# Group is an entity of type 1
# Each entity (user/group) has 0-1 email
# Each entity has 0-* phonenumbers
# If an entity is saved all its emails and phonenumbers are also saved
# If an entity is deleted all its emails and phonenumbers are also deleted
# When an entity is created and saved a current timestamp will be assigned to 'created' field
# When an entity is updated a current timestamp will be assigned to 'updated' field
# Entities will always be fetched in batches
<code type="php">
class Entity extends Doctrine_Record {
public function setUp() {
$this->ownsOne("Email","Entity.email_id");
$this->ownsMany("Phonenumber","Phonenumber.entity_id");
$this->setAttribute(Doctrine::ATTR_FETCHMODE,Doctrine::FETCH_BATCH);
$this->setAttribute(Doctrine::ATTR_LISTENER,new EntityListener());
}
public function setTableDefinition() {
$this->hasColumn("name","string",50);
$this->hasColumn("loginname","string",20);
$this->hasColumn("password","string",16);
$this->hasColumn("type","integer",1);
$this->hasColumn("created","integer",11);
$this->hasColumn("updated","integer",11);
$this->hasColumn("email_id","integer");
}
}
class Group extends Entity {
public function setUp() {
parent::setUp();
$this->hasMany("User","Groupuser.user_id");
$this->setInheritanceMap(array("type"=>1));
}
}
class User extends Entity {
public function setUp() {
parent::setUp();
$this->hasMany("Group","Groupuser.group_id");
$this->setInheritanceMap(array("type"=>0));
}
}
class Groupuser extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("group_id","integer");
$this->hasColumn("user_id","integer");
}
}
class Phonenumber extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("phonenumber","string",20);
$this->hasColumn("entity_id","integer");
}
}
class Email extends Doctrine_Record {
public function setTableDefinition() {
$this->hasColumn("address","string",150,"email|unique");
}
}
class EntityListener extends Doctrine_EventListener {
public function onPreUpdate(Doctrine_Record $record) {
$record->updated = time();
}
public function onPreInsert(Doctrine_Record $record) {
$record->created = time();
}
}
// USER MANAGEMENT SYSTEM IN ACTION:
$manager = Doctrine_Manager::getInstance();
$conn = $manager->openConnection(new PDO("DSN","username","password"));
$user = new User();
$user->name = "Jack Daniels";
$user->Email->address = "jackdaniels@drinkmore.info";
$user->Phonenumber[0]->phonenumber = "123 123";
$user->Phonenumber[1]->phonenumber = "133 133";
$user->save();
$user->Group[0]->name = "beer lovers";
$user->Group[0]->Email->address = "beerlovers@drinkmore.info";
$user->Group[0]->save();
</code>
+ Getting started
++ Requirements
++ Installation
++ Compiling
++ Starting new project
++ Working with existing databases
++ Exporting classes
+ Connection management
++ DSN, the Data Source Name
++ Opening a new connection
++ Lazy-connecting to database
++ Managing connections
++ Connection-component binding
+ Object relational mapping
++ Introduction
++ Table and class naming
++ Table options
++ Columns
++ Constraints and validators
++ Record identifiers
+++ Introduction
+++ Autoincremented
+++ Natural
+++ Composite
+++ Sequence
++ Indexes
+++ Introduction
+++ Adding indexes
+++ Index options
+++ Special indexes
++ Relations
+++ Introduction
+++ Relation aliases
+++ Foreign key associations
++++ One-to-One
++++ One-to-Many, Many-to-One
++++ Tree structure
+++ Join table associations
++++ One-to-One
++++ One-to-Many, Many-to-One
++++ Many-to-Many
++++ Self-referencing
+++ Inheritance
++++ One table many classes
++++ One table one class
++++ Column aggregation
+++ Foreign key constraints
++++ Introduction
++++ Constraint actions
++ Hierarchical data
+++ Introduction
++++ About
++++ Setting up
++++ Node interface
++++ Tree interface
++++ Traversing or Walking Trees
++++ Read me
+++ Adjacency list
++++ Introduction
+++ Nested set
++++ Introduction
++++ Setting up
++++ Tree options
++++ Node support
++++ Tree support
++++ Read me
+++ Materialized path
++++ Introduction
+++ Examples
+ Working with objects
++ Dealing with relations
+++ Creating related records
+++ Retrieving related records
+++ Updating related records
+++ Deleting related records
+++ Working with associations
++ Component overview
+++ Manager
++++ Introduction
++++ Opening a new connection
++++ Managing connections
+++ Connection
++++ Introduction
++++ Available drivers
++++ Getting a table object
++++ Flushing the connection
++++ Querying the database
++++ Getting connection state
+++ Record
++++ Introduction
++++ Creating new records
++++ Retrieving existing records
++++ Accessing properties
++++ Updating records
++++ Deleting records
++++ Getting record state
++++ Getting object copy
++++ Serializing
++++ Checking Existence
++++ Callbacks
+++ Collection
++++ Introduction
++++ Accessing elements
++++ Adding new elements
++++ Getting collection count
++++ Saving the collection
++++ Deleting collection
++++ Key mapping
++++ Loading related records
++++ Collection expanding
+++ Table
++++ Introduction
++++ Getting table information
++++ Finder methods
++++ Custom table classes
++++ Custom finders
++++ Getting relation objects
++ Fetching objects
+ Configuration
++ Introduction
++ Levels of configuration
++ Setting attributes
+++ Portability
+++ Identifier quoting
+++ Table creation
+++ Fetching strategy
+++ Batch size
+++ Session lockmode
+++ Event listener
+++ Validation
+++ Offset collection limit
+ Advanced components
++ Eventlisteners
+++ Introduction
+++ Creating new listener
+++ List of events
+++ Listening events
+++ Chaining
+++ AccessorInvoker
+++ Creating a logger
++ Validators
+++ Introduction
+++ More Validation
+++ Valid or Not Valid
+++ List of predefined validators
++ View
+++ Intoduction
+++ Managing views
+++ Using views
++ Cache
+++ Introduction
+++ Query cache
++ Locking Manager
+++ Introduction
+++ Examples
+++ Planned
+++ Technical Details
+++ Maintainer
++ Db_Profiler
+++ Introduction
+++ Basic usage
+++ Advanced usage
++ Hook
+++ Introduction
+++ Building queries
+++ List of parsers
++ Query
+++ Introduction
+++ selecting tables
+++ limiting the query results
+++ setting query conditions
+++ HAVING conditions
+++ sorting query results
++ RawSql
+++ Introduction
+++ Using SQL
+++ Adding components
+++ Method overloading
++ Db
+++ Introduction
+++ Connecting to a database
+++ Using event listeners
+++ Chaining listeners
++ Exceptions
+++ Overview
+++ List of exceptions
+ DQL (Doctrine Query Language)
++ Introduction
++ SELECT queries
+++ DISTINCT keyword
+++ Aggregate values
++ UPDATE queries
++ DELETE queries
++ FROM clause
++ WHERE clause
++ Conditional expressions
+++ Literals
+++ Input parameters
+++ Operators and operator precedence
+++ Between expressions
+++ In expressions
+++ Like Expressions
+++ Null Comparison Expressions
+++ Empty Collection Comparison Expressions
+++ Collection Member Expressions
+++ Exists Expressions
+++ All and Any Expressions
+++ Subqueries
++ Functional Expressions
+++ String functions
+++ Arithmetic functions
+++ Datetime functions
+++ Collection functions
++ Subqueries
+++ Introduction
+++ Comparisons using subqueries
+++ Conditional expressions
++++ ANY, IN and SOME
++++ ALL
++++ EXISTS and NOT EXISTS
+++ Correlated subqueries
+++ Subqueries in FROM clause
++ GROUP BY, HAVING clauses
++ ORDER BY clause
+++ Introduction
+++ Sorting by an aggregate value
+++ Using random order
++ LIMIT and OFFSET clauses
+++ Introduction
+++ Driver portability
+++ The limit-subquery-algorithm
++ Examples
++ BNF
+ Native SQL
++ Scalar queries
++ Component queries
++ Fetching multiple components
+ Transactions
++ Introduction
++ Unit of work
++ Nesting
++ Savepoints
++ Locking strategies
+++ Pessimistic locking
+++ Optimistic locking
++ Lock modes
++ Isolation levels
++ Deadlocks
+ Caching
++ Introduction
++ Availible options
++ Drivers
+++ Memcache
+++ APC
+++ Sqlite
+ Database abstraction
++ Modules
+++ Export
++++ Introduction
++++ Creating new table
++++ Altering table
+++ Import
++++ Introduction
++++ Getting table info
++++ Getting foreign key info
++++ Getting view info
+++ Util
++++ Using explain
+++ DataDict
++++ Getting portable type
++++ Getting database declaration
++++ Reserved keywords
++ Drivers
+++ Oracle
++++ Making unsuported functions work
+++ Mysql
++++ Tips and tricks
+ Technology
++ Architecture
++ Design patterns used
++ Speed
++ Internal optimizations
+++ DELETE
+++ INSERT
+++ UPDATE
+ Real world examples
++ User management system
++ Forum application
++ Album lister
+ Coding standards
++ Overview
+++ Scope
+++ Goals
++ PHP File Formatting
+++ General
+++ Indentation
+++ Maximum line length
+++ Line termination
++ Naming Conventions
+++ Classes
+++ Interfaces
+++ Filenames
+++ Functions and methods
+++ Variables
+++ Constants
+++ Record columns
++ Coding Style
+++ PHP code demarcation
+++ Strings
+++ Arrays
+++ Classes
+++ Functions and methods
+++ Control statements
+++ Inline documentation
++ Testing
+++ Writing tests
This diff is collapsed.
<?php
error_reporting(E_ALL);
$includePath = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'vendor';
set_include_path($includePath);
require_once('Sensei/Sensei.php');
require_once('DocTool.php');
spl_autoload_register(array('Sensei', 'autoload'));
$tool = new DocTool('docs/en/root.txt');
// $tool->setOption('clean-url', true);
$supportedLangs = array('en', 'fi');
foreach ($supportedLangs as $language) {
include "lang/$language.php";
$tool->addLanguage($lang[$language], $language);
}
$baseUrl = '';
$title = 'Doctrine Documentation';
$section = null;
if (isset($_GET['chapter'])) {
$section = $tool->findByPath($_GET['chapter']);
if ($tool->getOption('clean-url')) {
$baseUrl = '../';
}
}
if (isset($_GET['one-page'])) {
$tool->setOption('one-page', true);
$tool->setOption('max-level', 0);
$section = null;
$baseUrl = '';
}
if ($section) {
while ($section->getLevel() > 1) {
$section = $section->getParent();
}
$tool->setOption('section', $section);
$title .= ' - Chapter ' . $section->getIndex() . ' ' . $section->getName();
}
if ($tool->getOption('clean-url')) {
$tool->setOption('base-url', $baseUrl);
}
include 'template.php';
<?php
$lang['en'] = array(
'Chapter %s' => 'Chapter %s',
'Contents' => 'Contents'
);
<?php
$lang['fi'] = array(
'Table of Contents' => 'Sisällysluettelo',
'Contents' => 'Sisältö',
'List of Chapters' => 'Luvut',
'Previous' => 'Edellinen',
'Next' => 'Seuraava'
);
var symbolClosed = '+';
var symbolOpen = '-';
//var symbolClosed = '▹';
//var symbolOpen = '▿';
function Tree_AutoInit()
{
var candidates = document.getElementsByTagName('ul');
for (i in candidates) {
if (HasClassName(candidates[i], 'tree')) {
Tree_Init(candidates[i]);
}
}
}
function Tree_Init(element)
{
for (var i in element.childNodes) {
var li = element.childNodes[i];
if (li.tagName && li.tagName.toLowerCase() == 'li') {
var subTree = Tree_FindChild(li, 'ul');
if (subTree) {
var expander = document.createElement('a');
expander.className = 'expander';
expander.href = 'javascript:void(0);';
expander.onclick = Tree_Toggle;
if (HasClassName(subTree, 'closed')) {
expander.innerHTML = symbolClosed;
} else {
expander.innerHTML = symbolOpen;
}
li.insertBefore(expander, li.firstChild);
Tree_Init(subTree);
}
}
}
}
function Tree_FindChild(element, childTag)
{
for (i in element.childNodes) {
child = element.childNodes[i];
if (child.tagName && child.tagName.toLowerCase() == childTag) {
return child;
}
}
return null;
}
function Tree_Toggle()
{
expander = this;
li = expander.parentNode;
subTree = Tree_FindChild(li, 'ul');
if (HasClassName(subTree, 'closed')) {
RemoveClassName(subTree, 'closed');
expander.innerHTML = symbolOpen;
} else {
AddClassName(subTree, 'closed');
expander.innerHTML = symbolClosed;
}
}
// ----------------------------------------------------------------------------
// HasClassName
//
// Description : returns boolean indicating whether the object has the class name
// built with the understanding that there may be multiple classes
//
// Arguments:
// objElement - element to manipulate
// strClass - class name to add
//
function HasClassName(objElement, strClass)
{
// if there is a class
if ( objElement.className )
{
// the classes are just a space separated list, so first get the list
var arrList = objElement.className.split(' ');
// get uppercase class for comparison purposes
var strClassUpper = strClass.toUpperCase();
// find all instances and remove them
for ( var i = 0; i < arrList.length; i++ )
{
// if class found
if ( arrList[i].toUpperCase() == strClassUpper )
{
// we found it
return true;
}
}
}
// if we got here then the class name is not there
return false;
}
// ----------------------------------------------------------------------------
// AddClassName
//
// Description : adds a class to the class attribute of a DOM element
// built with the understanding that there may be multiple classes
//
// Arguments:
// objElement - element to manipulate
// strClass - class name to add
//
function AddClassName(objElement, strClass, blnMayAlreadyExist)
{
// if there is a class
if (objElement.className) {
// the classes are just a space separated list, so first get the list
var arrList = objElement.className.split(' ');
// if the new class name may already exist in list
if (blnMayAlreadyExist) {
// get uppercase class for comparison purposes
var strClassUpper = strClass.toUpperCase();
// find all instances and remove them
for (var i = 0; i < arrList.length; i++) {
// if class found
if (arrList[i].toUpperCase() == strClassUpper) {
// remove array item
arrList.splice(i, 1);
// decrement loop counter as we have adjusted the array's contents
i--;
}
}
}
// add the new class to end of list
arrList[arrList.length] = strClass;
// add the new class to beginning of list
//arrList.splice(0, 0, strClass);
// assign modified class name attribute
objElement.className = arrList.join(' ');
// if there was no class
} else {
// assign modified class name attribute
objElement.className = strClass;
}
}
// ----------------------------------------------------------------------------
// RemoveClassName
//
// Description : removes a class from the class attribute of a DOM element
// built with the understanding that there may be multiple classes
//
// Arguments:
// objElement - element to manipulate
// strClass - class name to remove
//
function RemoveClassName(objElement, strClass)
{
// if there is a class
if ( objElement.className )
{
// the classes are just a space separated list, so first get the list
var arrList = objElement.className.split(' ');
// get uppercase class for comparison purposes
var strClassUpper = strClass.toUpperCase();
// find all instances and remove them
for ( var i = 0; i < arrList.length; i++ )
{
// if class found
if ( arrList[i].toUpperCase() == strClassUpper )
{
// remove array item
arrList.splice(i, 1);
// decrement loop counter as we have adjusted the array's contents
i--;
}
}
// assign modified class name attribute
objElement.className = arrList.join(' ');
}
// if there was no class
// there is nothing to remove
}
/*
* Handlers for automated loading
*/
_LOADERS = Array();
function callAllLoaders() {
var i, loaderFunc;
for(i=0;i<_LOADERS.length;i++) {
loaderFunc = _LOADERS[i];
if(loaderFunc != callAllLoaders) loaderFunc();
}
}
function appendLoader(loaderFunc) {
if(window.onload && window.onload != callAllLoaders)
_LOADERS[_LOADERS.length] = window.onload;
window.onload = callAllLoaders;
_LOADERS[_LOADERS.length] = loaderFunc;
}
appendLoader(Tree_AutoInit);
body, html
{
margin: 0;
padding: 0;
font-family: "Arial", sans-serif;
font-size: 11pt;
background: white;
color: black;
}
h1, h2, h3, h4, h5, h6
{
font-family: "Lucida Bright", "Times New Roman", serif;
}
h1
{
text-align: center;
font-size: 24pt;
}
h2, #table-of-contents h1 {
font-size: 18pt;
text-align: left;
}
h3 {
font-size: 14pt;
}
p, ul, ol
{
line-height: 1.5em;
text-align: justify;
}
ul
{
list-style-type: square;
}
ul ul, ol ol {
font-size: 100%;
}
table
{
border: 1px solid #aaaaaa;
border-collapse: collapse;
margin: 0.5em 0;
font-size: 11pt;
background-color: #f9f9f9;
line-height: 1.5em;
}
td, th
{
padding: 0.2em;
border: 1px solid #aaaaaa;
}
th
{
background-color: #f2f2f2;
}
blockquote
{
background-image: url("../images/quotation-mark.png");
background-position: top left;
background-repeat: no-repeat;
padding-left: 2.5em;
margin-left: 1em;
}
/*** Code blocks and highlighting**********************************************/
pre, tt
{
font-family: "Bitstream Vera Sans Mono", monospace;
}
tt
{
font-size: 11pt;
}
pre
{
font-size: 10pt;
font-weight: bold;
padding: 1em;
margin: 1em;
line-height: 1.2em;
background-color: #f6f6f6;
border: 1px solid #cccccc;
overflow: auto;
}
pre .default
{
color: #000000;
}
pre .keyword
{
color: #3465a4;
}
pre .string
{
color: #ef2929;
}
pre .comment
{
color: #888a85;
}
a:link
{
color: #3465a4;
text-decoration: none;
}
a:visited
{
color: #75507b;
text-decoration: none;
}
a:link:hover, a:link:active
{
text-decoration: underline;
color: #729fcf;
}
a:visited:hover, a:visited:active
{
text-decoration: underline;
color: #ad7fa8;
}
#table-of-contents {
position: absolute;
top: 0;
right: 0;
width: 20em;
overflow: auto;
border: 1px solid #cccccc;
background-color: #f6f6f6;
padding: 0 1em;
margin: 1em;
max-height: 95%;
}
#wrap {
position: absolute;
left: 0;
top: 0;
width: 100%;
margin: 0;
}
#sidebar {
position: fixed;
top: 0;
right: 0;
height: 100%;
}
#content {
position: absolute;
top: 0;
left: 0;
right: 23em;
margin: 0;
padding: 1em;
}
ul.tree, ul.tree ul {
list-style-type: none;
padding-left: 2em;
text-align: left;
margin-left: 0;
}
/** Tree specific styles ******************************************************/
.tree a {
text-decoration: none;
}
.tree .expander {
color: black;
display: block;
float: left;
height: auto;
margin-left: -1.25em;
padding-left: 0;
text-align: center;
width: 1em;
}
.tree .expander:link,
.tree .expander:visited,
.tree .expander:active,
.tree .expander:hover {
color: black;
text-decoration: none;
}
.tree ul.closed {
display: none;
}
#sidebar {
/* IE5.5+/Win - this is more specific than the IE 5.0 version */
left: auto; bottom: auto;
right: expression( ( 20 + ( ignoreMe2 = document.documentElement.scrollRight ? document.documentElement.scrollRight : document.body.scrollRight ) ) + 'px' );
top: expression( ( 10 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
\ No newline at end of file
body {
font-family: "Arial", sans-serif;
font-size: 11pt;
}
.chapter, #table-of-contents {
page-break-after: always;
}
h1, h2, h3, h4, h5, h6 {
font-family: "Lucida Bright", serif;
}
h1 {
text-align: center;
}
p, ul, ol {
text-align: justify;
line-height: 1.2em;
}
ol {
list-style-type: square;
}
pre, tt {
font-family: "Bitstream Vera Sans Mono", monospace;
font-size: 10pt;
}
table {
border: thin solid black;
border-collapse: collapse;
}
td, th {
border: thin solid black;
padding: 0.5em;
}
.expander {
display: none;
}
a {
text-decoration: none;
font: inherit;
color: inherit;
}
#table-of-contents {
display: none;
}
.one-page#table-of-contents {
display: block;
}
#table-of-contents ul {
list-style-type: none;
}
ul.tree, ul.tree ul {
list-style-type: none;
padding-left: 2em;
text-align: left;
margin-left: 0;
}
.tree a {
text-decoration: none;
}
.tree .expander {
color: black;
display: block;
float: left;
height: auto;
margin-left: -1.25em;
padding-left: 0;
text-align: center;
width: 1em;
}
.tree .expander:link,
.tree .expander:visited,
.tree .expander:active,
.tree .expander:hover {
color: black;
text-decoration: none;
}
.tree ul.closed {
display: none;
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><?php echo $title; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="<?php echo $baseUrl; ?>styles/basic.css" media="screen"/>
<link rel="stylesheet" type="text/css" href="<?php echo $baseUrl; ?>styles/print.css" media="print"/>
<!--[if gte IE 5.5]>
<![if lt IE 7]>
<link rel="stylesheet" type="text/css" href="<?php echo $baseUrl; ?>styles/iefix.css"/>
<![endif]>
<![endif]-->
<script type="text/javascript" src="<?php echo $baseUrl; ?>scripts/tree.js"></script>
</head>
<body>
<div id="wrap">
<div id="sidebar">
<?php $tool->renderToc(); ?>
</div>
<div id="content">
<?php
try {
$tool->render();
} catch (Exception $e) {
?>
<h1>Doctrine Documentation</h1>
<p>You can view this documentation as
<ul>
<li><a href="<?php echo $tool->getOption('clean-url') ? "${baseUrl}one-page" : '?one-page=1'; ?>">everything on a single page</a>, or</li>
<li><a href="<?php echo $tool->makeUrl($tool->findByIndex('1.')->getPath()); ?>">one chapter per page</a>.</li>
</ul>
</p>
<?php
}
?>
</div>
</div>
</body>
</html>
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
*/
/**
* Sensei
*
* @package Sensei
* @category Core
* @license http://www.gnu.org/licenses/lgpl.txt LGPL
* @link http://sourceforge.net/projects/sensei
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @version $Revision$
* @since 1.0
*/
final class Sensei {
/**
* @var string $path doctrine root directory
*/
private static $path;
/**
* getPath
* returns the doctrine root
*
* @return string
*/
public static function getPath()
{
if (! self::$path) {
self::$path = dirname(__FILE__);
}
return self::$path;
}
/**
* simple autoload function
* returns true if the class was loaded, otherwise false
*
* @param string $classname
* @return boolean
*/
public static function autoload($classname)
{
if (class_exists($classname, false)) {
return false;
}
if ( ! self::$path) {
self::$path = dirname(__FILE__);
}
$class = self::$path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $classname) . '.php';
if ( ! file_exists($class)) {
return false;
}
require_once($class);
return true;
}
/**
* Load a given class: a file name is acquired by naively transforming
* underscores into directory separators and appending the .php suffix.
*
* The file is searched for in every directory in the include path.
*
* @param string class name
* @param boolean allow class to be autoloaded before attempt
* @return true
* @throws Xi_Exception if the class could not be loaded
*/
public static function loadClass($className)
{
if (class_exists($className, false)) {
return false;
}
if (! self::$path) {
self::$path = dirname(__FILE__);
}
$class = self::$path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
if ( ! file_exists($class)) {
return false;
}
require_once($class);
throw new Sensei_Exception('Class ' . $className . ' does not exist and could not '
. 'be loaded.');
}
/**
* Create a new instance of a class.
*
* @param string class name
* @param array constructor arguments, optional
* @return object
*/
public static function create($class, array $args = array())
{
/**
* An arbitrary amount of constructor arguments can be achieved using
* reflection, but it's slower by an order of magnitude. Manually handle
* instantiation for up to three arguments.
*/
switch (count($args)) {
case 0:
return new $class;
case 1:
return new $class($args[0]);
case 2:
return new $class($args[0], $args[1]);
case 3:
return new $class($args[0], $args[1], $args[2]);
default:
return call_user_func_array(array(new ReflectionClass($class),'newInstance'),
$args);
}
}
}
This diff is collapsed.
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://sourceforge.net/projects/sensei>.
*/
/**
* Sensei_Doc_Toc
*
* @package Sensei_Doc
* @category Documentation
* @license http://www.gnu.org/licenses/lgpl.txt LGPL
* @link http://sourceforge.net/projects/sensei
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @version $Revision$
* @since 1.0
*/
class Sensei_Doc_Toc implements Countable
{
/**
* An empty (root) section that contains all other sections.
*
* @var Sensei_Doc_Section
*/
private $_toc;
/**
* Constructs a new table of contents from a file
*
* @param string $filename Name of the file that contains the section
* structure.
*/
public function __construct($filename)
{
$this->_toc = new Sensei_Doc_Section();
$this->_toc->parse(dirname($filename), basename($filename));
}
/**
* Finds the section that matches the given path.
*
* The path consists of section names, where spaces are replaced by
* underscores, and which separated by '/' (default).
*
* @param string $path Path
* @param string $separator A string that separates section names in path.
* @return Sensei_Doc_Section|null A section that matches the given path, or
* null if no matching section was found.
*/
public function findByPath($path, $separator = ':')
{
$sectionPaths = explode($separator, $path);
$currentSection = $this->_toc;
foreach ($sectionPaths as $sectionPath) {
$found = false;
for ($i = 0; $i < $currentSection->count(); $i++) {
if ($currentSection->getChild($i)->getPath(true, $separator) === $sectionPath) {
$currentSection = $currentSection->getChild($i);
$found = true;
break;
}
}
if ( ! $found) {
return null;
}
}
return $currentSection;
}
public function findByIndex($index, $separator = '.')
{
$indexes = explode($separator, $index);
$currentSection = $this->_toc;
if (end($indexes) === '') {
array_pop($indexes);
}
foreach ($indexes as $i) {
try {
$currentSection = $currentSection->getChild((int) $i - 1);
} catch (Exception $e) {
return null;
}
}
return $currentSection;
}
/**
* Returns a root section with the given index.
*
* @param int $index
* @return Sensei_Doc_Section
*/
public function getChild($index)
{
return $this->_toc->getChild($index);
}
/**
* Returns the number of sections (excluding their subsections).
*
* @return int
*/
public function count()
{
return $this->_toc->count();
}
}
\ No newline at end of file
......@@ -46,11 +46,11 @@ class Text_Wiki_Render_Xhtml_Bold extends Text_Wiki_Render {
{
if ($options['type'] == 'start') {
$css = $this->formatConf(' class="%s"', 'css');
return "<b$css>";
return "<strong$css>";
}
if ($options['type'] == 'end') {
return '</b>';
return '</strong>';
}
}
}
......
......@@ -59,38 +59,39 @@ class Text_Wiki_Render_Xhtml_Code extends Text_Wiki_Render {
$css_filename = $this->formatConf(' class="%s"', 'css_filename');
if ($type == 'php') {
if (substr($options['text'], 0, 5) != '<?php') {
// PHP code example:
// add the PHP tags
$text = "<?php
" . $options['text'] . "\n?>"; // <?php
$text = "<?php \n"
. $options['text'] . "\n\n"
. "?>";
}
// convert tabs to four spaces
$text = str_replace("\t", "&nbsp;&nbsp;%nbsp;&nbsp;", $text);
$text = str_replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;", $text);
// colorize the code block (also converts HTML entities and adds
// <code>...</code> tags)
$h = new PHP_Highlight;
$h = new PHP_Highlight(true);
$h->loadString($text);
$text = $h->toHtml(true);
// replace <br /> tags with simple newlines.
// replace non-breaking space with simple spaces.
// translate HTML <font> and color to XHTML <span> and style.
// courtesy of research by A. Kalin :-).
/**
$map = array(
'<br />' => "\n",
'&nbsp;' => ' ',
"\n" => "<br \>",
'<font' => '<span',
'</font>' => '</span>',
'color="' => 'style="color:'
'&nbsp;' => ' '
//'<br />' => "\n",
//"\n" => "<br \>",
//'<font' => '<span',
//'</font>' => '</span>',
//'color="' => 'style="color:'
);
$text = strtr($text, $map);
*/
// get rid of the last newline inside the code block
// (becuase higlight_string puts one there)
......@@ -104,10 +105,10 @@ class Text_Wiki_Render_Xhtml_Code extends Text_Wiki_Render {
}
// done
$text = "<table border=1 class='dashed' cellpadding=0 cellspacing=0>
/*$text = "<table border=1 class='dashed' cellpadding=0 cellspacing=0>
<tr><td><b>$text
</b></td></tr>
</table>";
</table>";*/
} elseif ($type == 'html' || $type == 'xhtml') {
......@@ -116,9 +117,9 @@ class Text_Wiki_Render_Xhtml_Code extends Text_Wiki_Render {
// convert tabs to four spaces,
// convert entities.
$text = str_replace("\t", " ", $text);
$text = "<html>\n$text\n</html>";
//$text = "<html>\n$text\n</html>";
$text = $this->textEncode($text);
$text = "<pre$css><code$css_html>$text</code></pre>";
$text = "<pre$css>$text</pre>";
} elseif ($type == 'sql') {
// HTML code example:
......@@ -127,14 +128,14 @@ class Text_Wiki_Render_Xhtml_Code extends Text_Wiki_Render {
// convert entities.
$text = str_replace("\t", " ", $text);
$text = $this->textEncode($text);
$text = "<div class='sql'>$text</div>";
$text = "<pre class=\"sql\">$text</pre>";
} else {
// generic code example:
// convert tabs to four spaces,
// convert entities.
$text = str_replace("\t", " ", $text);
$text = $this->textEncode($text);
$text = "<pre$css><code$css_code>$text</code></pre>";
$text = "<pre$css>$text</pre>";
}
if ($css_filename && isset($attr['filename'])) {
......
......@@ -46,11 +46,11 @@ class Text_Wiki_Render_Xhtml_Italic extends Text_Wiki_Render {
{
if ($options['type'] == 'start') {
$css = $this->formatConf(' class="%s"', 'css');
return "<i$css>";
return "<em$css>";
}
if ($options['type'] == 'end') {
return '</i>';
return '</em>';
}
}
}
......
......@@ -46,11 +46,11 @@ class Text_Wiki_Render_Xhtml_Underline extends Text_Wiki_Render {
{
if ($options['type'] == 'start') {
$css = $this->formatConf(' class="%s"', 'css');
return "<u$css>";
return "<span style=\"text-decoration: underline;\"$css>";
}
if ($options['type'] == 'end') {
return '</u>';
return '</span>';
}
}
}
......
......@@ -93,6 +93,7 @@ class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render {
$css = $this->formatConf(' class="%s"', "css_$type");
$output = "<a$css href=\"$href\"";
/*
if ($target) {
// use a "popup" window. this is XHTML compliant, suggested by
// Aaron Kalin. uses the $target as the new window name.
......@@ -100,6 +101,7 @@ class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render {
$output .= " onclick=\"window.open(this.href, '$target');";
$output .= " return false;\"";
}
*/
// finish up output
$output .= ">$text</a>";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment