Importing bits and pieces
This is the initial import based on a few files I had around.
This commit is contained in:
commit
871d28cd16
20 changed files with 1994 additions and 0 deletions
44
includes/core/controller.inc.php
Normal file
44
includes/core/controller.inc.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
class ParameterException extends Exception { }
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
|
||||
protected final function getParameter( $name , $method = null )
|
||||
{
|
||||
if ( $method === null ) {
|
||||
try {
|
||||
return $this->getParameter( $name , 'POST' );
|
||||
} catch ( ParameterException $e ) {
|
||||
return $this->getParameter( $name , 'GET' );
|
||||
}
|
||||
}
|
||||
|
||||
$from = '_' . $method;
|
||||
global $$from;
|
||||
if ( ! array_key_exists( $name , $$from ) ) {
|
||||
throw new ParameterException( "$name/$method" );
|
||||
}
|
||||
return ${$from}[ $name ];
|
||||
}
|
||||
|
||||
public abstract function handle( Page $page );
|
||||
}
|
||||
|
||||
|
||||
final class Ctrl_Simple
|
||||
extends Controller
|
||||
{
|
||||
private $view;
|
||||
|
||||
public function __construct( View $view )
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
return $this->view;
|
||||
}
|
||||
}
|
20
includes/core/dao.inc.php
Normal file
20
includes/core/dao.inc.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
abstract class DAO
|
||||
{
|
||||
private $database;
|
||||
|
||||
public final function setDatabase( Database $database )
|
||||
{
|
||||
if ( $this->database !== null ) {
|
||||
throw new Exception( "trying to change DAO database" );
|
||||
}
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
protected final function query( $query , $prepare = false )
|
||||
{
|
||||
return $this->database->query( $query , $prepare );
|
||||
}
|
||||
}
|
||||
|
133
includes/core/database.inc.php
Normal file
133
includes/core/database.inc.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
class DatabaseError extends RuntimeException { }
|
||||
|
||||
final class Database
|
||||
implements PackageAware
|
||||
{
|
||||
|
||||
private $connection;
|
||||
private $package;
|
||||
private $queries = array( );
|
||||
|
||||
public function setPackage( Package $package )
|
||||
{
|
||||
if ( $this->package !== null ) {
|
||||
throw new Exception( 'trying to call setPackage() twice' );
|
||||
}
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
|
||||
public function query( $query , $prepare = false )
|
||||
{
|
||||
if ( ! $this->connection ) {
|
||||
$this->connect( );
|
||||
}
|
||||
if ( ! array_key_exists( $query , $this->queries )
|
||||
|| ( $prepare && ! $this->queries[ $query ]->prepared( ) ) ) {
|
||||
$this->queries[ $query ] = new DBQuery( $this->connection , $query , $prepare );
|
||||
}
|
||||
return $this->queries[ $query ];
|
||||
}
|
||||
|
||||
public function commit( )
|
||||
{
|
||||
if ( ! $this->connection ) {
|
||||
return;
|
||||
}
|
||||
if ( ! @pg_query( 'COMMIT' ) ) {
|
||||
throw new DatabaseError( 'COMMIT: ' . pg_last_error( ) );
|
||||
}
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
public function connect( )
|
||||
{
|
||||
$host = $this->package->config( 'db/host' , 'localhost' );
|
||||
$port = $this->package->config( 'db/port' );
|
||||
$name = $this->package->config( 'db/name' , null , true );
|
||||
$user = $this->package->config( 'db/user' );
|
||||
$pass = $this->package->config( 'db/password' );
|
||||
|
||||
$cString = array( );
|
||||
$cString[] = "host=$host";
|
||||
if ( $port !== null ) {
|
||||
$cString[] = "port=$port";
|
||||
}
|
||||
$cString[] = "dbname=$name";
|
||||
if ( $user !== null ) {
|
||||
$cString[] = "user=$user";
|
||||
}
|
||||
if ( $pass !== null ) {
|
||||
$cString[] = "password=$pass";
|
||||
}
|
||||
|
||||
$this->connection = pg_connect( join( ' ' , $cString ) );
|
||||
if ( ! $this->connection ) {
|
||||
throw new DatabaseError( 'connection failed' );
|
||||
}
|
||||
|
||||
if ( ! @pg_query( $this->connection , 'BEGIN TRANSACTION' ) ) {
|
||||
throw new DatabaseError( 'BEGIN TRANSACTION: ' . pg_last_error( ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class DBQuery
|
||||
{
|
||||
private static $lastStatementID = 0;
|
||||
|
||||
private $connection;
|
||||
private $query;
|
||||
private $statement;
|
||||
|
||||
|
||||
public function __construct( $connection , $query , $prepare = false )
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->query = $query;
|
||||
if ( $prepare ) {
|
||||
$this->statement = 'prep_stmt_' . ( ++ DBQuery::$lastStatementID );
|
||||
if ( ! pg_prepare( $connection , $this->statement , $query ) ) {
|
||||
throw new Exception( "unable to prepare statement '$query': " . pg_last_error( ) );
|
||||
}
|
||||
} else {
|
||||
$this->statement = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct( )
|
||||
{
|
||||
if ( $this->statement !== null ) {
|
||||
if ( ! pg_query( 'DEALLOCATE ' . $this->statement ) ) {
|
||||
throw new Exception( "unable to deallocate statement: " . pg_last_error( ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function execute( )
|
||||
{
|
||||
$arguments = func_get_args( );
|
||||
$result = array( );
|
||||
|
||||
if ( $this->statement !== null ) {
|
||||
$pgResult = pg_execute( $this->connection , $this->statement , $arguments );
|
||||
} else {
|
||||
$pgResult = pg_query_params( $this->connection , $this->query , $arguments );
|
||||
}
|
||||
|
||||
if ( ! $pgResult ) {
|
||||
throw new Exception( "query \"{$this->query}\" failed: " . pg_last_error( ) );
|
||||
}
|
||||
|
||||
while ( $row = pg_fetch_object( $pgResult ) ) {
|
||||
array_push( $result , $row );
|
||||
}
|
||||
pg_free_result( $pgResult );
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
26
includes/core/package.inc.php
Normal file
26
includes/core/package.inc.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
$package[ 'extras' ][] = 'Controller';
|
||||
$package[ 'extras' ][] = 'DAO';
|
||||
$package[ 'extras' ][] = 'DatabaseError';
|
||||
$package[ 'extras' ][] = 'DBQuery';
|
||||
$package[ 'extras' ][] = 'HTML';
|
||||
$package[ 'extras' ][] = 'HTMLPage';
|
||||
$package[ 'extras' ][] = 'Page';
|
||||
$package[ 'extras' ][] = 'ParameterException';
|
||||
$package[ 'extras' ][] = 'URLMapper';
|
||||
$package[ 'extras' ][] = 'View';
|
||||
|
||||
$package[ 'files' ][] = 'controller';
|
||||
$package[ 'files' ][] = 'dao';
|
||||
$package[ 'files' ][] = 'database';
|
||||
$package[ 'files' ][] = 'page';
|
||||
$package[ 'files' ][] = 'urls';
|
||||
$package[ 'files' ][] = 'view';
|
||||
|
||||
$package[ 'pages' ][] = 'basic';
|
||||
|
||||
$package[ 'ctrls' ][] = 'session';
|
||||
$package[ 'ctrls' ][] = 'simple';
|
||||
|
||||
$package[ 'singletons' ][] = 'Database';
|
208
includes/core/page.inc.php
Normal file
208
includes/core/page.inc.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
|
||||
abstract class Page
|
||||
{
|
||||
private $baseURL;
|
||||
private $controllers = array( );
|
||||
protected $views = array( );
|
||||
|
||||
|
||||
public function __construct( )
|
||||
{
|
||||
$this->baseURL = dirname( $_SERVER[ 'SCRIPT_NAME' ] );
|
||||
}
|
||||
|
||||
public final function addController( Controller $controller )
|
||||
{
|
||||
array_push( $this->controllers , $controller );
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public final function addView( View $view )
|
||||
{
|
||||
array_push( $this->controllers , new Ctrl_Simple( $view ) );
|
||||
return $this;
|
||||
}
|
||||
|
||||
public final function getBaseURL( )
|
||||
{
|
||||
return $this->baseURL;
|
||||
}
|
||||
|
||||
|
||||
protected abstract function render( );
|
||||
|
||||
protected function handleControllerValue( $rc )
|
||||
{
|
||||
$rv = false;
|
||||
if ( is_a( $rc , 'View' ) ) {
|
||||
array_push( $this->views , $rc );
|
||||
} elseif ( is_a( $rc , 'Controller' ) ) {
|
||||
$rv = $this->executeController( $rc );
|
||||
} elseif ( is_array( $rc ) ) {
|
||||
foreach ( $rc as $rcItem ) {
|
||||
if ( $this->handleControllerValue( $rcItem ) ) {
|
||||
$rv = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif ( ! is_null( $rc ) ) {
|
||||
header( "Location: $rc" );
|
||||
$rv = true;
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
protected function executeController( Controller $controller )
|
||||
{
|
||||
return $this->handleControllerValue( $controller->handle( $this ) );
|
||||
}
|
||||
|
||||
public final function handle( )
|
||||
{
|
||||
$mustDraw = true;
|
||||
foreach ( $this->controllers as $controller ) {
|
||||
if ( $this->executeController( $controller ) ) {
|
||||
$mustDraw = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $mustDraw ) {
|
||||
$this->render( );
|
||||
}
|
||||
Loader::Singleton( 'Database' )->commit( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface PathAware
|
||||
{
|
||||
|
||||
public function setExtraPath( $path );
|
||||
|
||||
}
|
||||
|
||||
|
||||
abstract class HTMLPage
|
||||
extends Page
|
||||
implements PackageAware
|
||||
{
|
||||
protected $title;
|
||||
protected $package;
|
||||
|
||||
public function __construct( )
|
||||
{
|
||||
parent::__construct( );
|
||||
}
|
||||
|
||||
public function setPackage( Package $package )
|
||||
{
|
||||
if ( $this->package !== null ) {
|
||||
throw new Exception( 'trying to call setPackage() twice' );
|
||||
}
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
protected abstract function getMenu( );
|
||||
|
||||
|
||||
private function renderMenu( )
|
||||
{
|
||||
$menu = $this->getMenu( );
|
||||
if ( empty( $menu ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$html = HTML::make( 'ul' )
|
||||
->setAttribute( 'class' , 'page-menu' );
|
||||
|
||||
foreach ( $menu as $link => $title ) {
|
||||
$html->appendElement( HTML::make( 'li' )
|
||||
->appendElement( HTML::make( 'a' )
|
||||
->setAttribute( 'href' , $link )
|
||||
->setAttribute( 'title' , HTML::from( $title ) )
|
||||
->appendText( $title ) ) );
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
public function setTitle( $title )
|
||||
{
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function getHead( $title )
|
||||
{
|
||||
return HTML::make( 'head' )
|
||||
->appendElement( HTML::make( 'title' )
|
||||
->appendText( $title ) )
|
||||
->appendElement( HTML::make( 'meta' )
|
||||
->setAttribute( 'http-equiv' , 'content-type' )
|
||||
->setAttribute( 'content' , 'text/html;charset=UTF-8' ) )
|
||||
->appendElement( HTML::make( 'style' )
|
||||
->setAttribute( 'id' , 'main-style' )
|
||||
->setAttribute( 'class' , 'css-style' )
|
||||
->appendText( "\n@import url('" . $this->getBaseURL( )
|
||||
. "/style.css?1');\n" ) );
|
||||
}
|
||||
|
||||
|
||||
protected function getBody( $title )
|
||||
{
|
||||
$menu = $this->renderMenu( );
|
||||
$container = HTML::make( 'div' )
|
||||
->setAttribute( 'class' , 'page-container' );
|
||||
|
||||
$t = HTML::make( 'h1' )->appendText( $title );
|
||||
if ( is_null( $menu ) ) {
|
||||
$t->setAttribute( 'class' , 'no-menu' );
|
||||
}
|
||||
$container->appendElement( $t );
|
||||
|
||||
if ( !is_null( $menu ) ) {
|
||||
$container->append( $menu );
|
||||
}
|
||||
|
||||
foreach ( $this->views as $view ) {
|
||||
$container->append( $view->render( ) );
|
||||
}
|
||||
|
||||
return HTML::make( 'body' )->appendElement( $container );
|
||||
}
|
||||
|
||||
|
||||
public function render( )
|
||||
{
|
||||
$baseTitle = $this->package->config( 'pages/baseTitle' , null , false );
|
||||
if ( $baseTitle === null ) {
|
||||
$baseTitle = Loader::PackageConfig( 'core' )->get( 'pages/baseTitle' , '' , true );
|
||||
}
|
||||
$title = is_null( $this->title ) ? '' : ( ' - ' . $this->title );
|
||||
$pTitle = is_null( $this->title ) ? $baseTitle : $this->title;
|
||||
$title = $baseTitle . $title;
|
||||
|
||||
header( 'Content-type: text/html; charset=utf-8' );
|
||||
echo HTML::make( 'html' )
|
||||
->appendElement( $this->getHead( $title ) )
|
||||
->appendElement( $this->getBody( $pTitle ) )
|
||||
->getCode( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Page_Basic
|
||||
extends HTMLPage
|
||||
{
|
||||
|
||||
protected function getMenu( )
|
||||
{
|
||||
return array( );
|
||||
}
|
||||
|
||||
}
|
103
includes/core/urls.inc.php
Normal file
103
includes/core/urls.inc.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
|
||||
final class URLMapperException extends Exception {}
|
||||
|
||||
|
||||
final class URLMapper
|
||||
implements PackageAware
|
||||
{
|
||||
private $package;
|
||||
private $prefix;
|
||||
private $configBase;
|
||||
|
||||
public function __construct( $prefix = null )
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
$this->configBase = ( $prefix == null ) ? 'urls' : $prefix;
|
||||
}
|
||||
|
||||
public function setPackage( Package $package )
|
||||
{
|
||||
if ( $this->package !== null ) {
|
||||
throw new Exception( 'trying to call setPackage() twice' );
|
||||
}
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
|
||||
public function fromPathInfo( )
|
||||
{
|
||||
if ( array_key_exists( 'PATH_INFO' , $_SERVER ) ) {
|
||||
$path = $_SERVER[ 'PATH_INFO' ];
|
||||
} else {
|
||||
$path = '/' . $this->package->config( $this->configBase . '/default' , 'home' );
|
||||
}
|
||||
|
||||
$this->fromPath( $path );
|
||||
}
|
||||
|
||||
public function fromPath( $path )
|
||||
{
|
||||
if ( ! preg_match( '/^(\/[a-z0-9]+)+$/' , $path ) ) {
|
||||
$this->showPageNotFound( );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->prefix == null ) {
|
||||
$path = substr( $path , 1 );
|
||||
} else {
|
||||
$path = $this->prefix . $path;
|
||||
}
|
||||
try {
|
||||
$this->showPageFor( $path );
|
||||
} catch ( URLMapperException $e ) {
|
||||
$this->showPageNotFound( $path );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function showPageNotFound( $requestPath )
|
||||
{
|
||||
$path = $this->package->config( $this->configBase . '/errors/404', 'errors/404' );
|
||||
$this->showPageFor( $path . '/' . $requestPath , false );
|
||||
}
|
||||
|
||||
|
||||
private function showPageFor( $path )
|
||||
{
|
||||
$split = split( '/' , $path );
|
||||
$extras = array( );
|
||||
while ( !empty( $split ) ) {
|
||||
$name = join( '_' , $split );
|
||||
try {
|
||||
$page = Loader::Page( $name );
|
||||
break;
|
||||
} catch ( LoaderException $e ) {
|
||||
array_unshift( $extras , array_pop( $split ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $split ) ) {
|
||||
throw new URLMapperException( $path );
|
||||
}
|
||||
|
||||
$this->handlePage( $path , $page , join( '/' , $extras ) );
|
||||
}
|
||||
|
||||
|
||||
private function handlePage( $requestPath , Page $page , $extraPath = '' , $pathFailure = true )
|
||||
{
|
||||
if ( $page instanceof PathAware ) {
|
||||
$success = $page->setExtraPath( $extraPath );
|
||||
} else {
|
||||
$success = ( $extraPath === '' );
|
||||
}
|
||||
|
||||
if ( $pathFailure && !$success ) {
|
||||
$this->showPageNotFound( $requestPath );
|
||||
} else {
|
||||
$page->handle( );
|
||||
}
|
||||
}
|
||||
}
|
117
includes/core/view.inc.php
Normal file
117
includes/core/view.inc.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
interface View
|
||||
{
|
||||
public function render( );
|
||||
}
|
||||
|
||||
|
||||
final class HTML
|
||||
{
|
||||
protected $tag;
|
||||
protected $attributes = array( );
|
||||
protected $contents = array( );
|
||||
protected $cached;
|
||||
|
||||
|
||||
public function __construct( $tag )
|
||||
{
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
|
||||
public function setAttribute( $attribute , $value )
|
||||
{
|
||||
if ( $value !== null ) {
|
||||
$this->attributes[ $attribute ] = $value;
|
||||
$this->cached = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function appendText( $text )
|
||||
{
|
||||
assert( is_scalar( $text ) );
|
||||
array_push( $this->contents , HTML::from( $text ) );
|
||||
$this->cached = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function appendElement( HTML $element )
|
||||
{
|
||||
array_push( $this->contents , $element );
|
||||
$this->cached = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function appendRaw( $text )
|
||||
{
|
||||
assert( is_scalar( $text ) );
|
||||
array_push( $this->contents , $text );
|
||||
$this->cached = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function append( $auto )
|
||||
{
|
||||
if ( is_array( $auto ) ) {
|
||||
foreach ( $auto as $element ) {
|
||||
$this->append( $element );
|
||||
}
|
||||
} elseif ( is_scalar( $auto ) ) {
|
||||
$this->appendRaw( $auto );
|
||||
} else {
|
||||
$this->appendElement( $auto );
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getCode( )
|
||||
{
|
||||
if ( $this->cached !== null ) {
|
||||
return $this->cached;
|
||||
}
|
||||
$code = '<' . $this->tag;
|
||||
|
||||
if ( ! empty( $this->attributes ) ) {
|
||||
$attrs = array( );
|
||||
foreach ( $this->attributes as $name => $value ) {
|
||||
array_push( $attrs , $name . '="' . $value . '"' );
|
||||
}
|
||||
$code .= ' ' . join( ' ' , $attrs );
|
||||
}
|
||||
|
||||
if ( empty( $this->contents ) ) {
|
||||
$code .= ' />';
|
||||
} else {
|
||||
$code .= '>';
|
||||
foreach ( $this->contents as $item ) {
|
||||
if ( is_scalar( $item ) ) {
|
||||
$code .= $item;
|
||||
} else {
|
||||
$code .= $item->getCode( );
|
||||
}
|
||||
}
|
||||
$code .= '</' . $this->tag . '>';
|
||||
}
|
||||
|
||||
return ( $this->cached = $code );
|
||||
}
|
||||
|
||||
|
||||
public static function make( $tag )
|
||||
{
|
||||
return new HTML( $tag );
|
||||
}
|
||||
|
||||
|
||||
public static function from( $text )
|
||||
{
|
||||
return htmlentities( $text , ENT_COMPAT , 'UTF-8' );
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue