|
Book Titles |
So Long, and Thanks for All the Fish |
The Hitchhiker's Guide to the Galaxy |
Book Titles |
Foundation |
Foundation & Empire |
I, Robot |
Second Foundation |
Book Titles |
James and the Giant Peach |
Tales of the Unexpected |
Book Titles |
David Copperfield |
Nicholas Nickleby |
Oliver Twist |
Book Titles |
Starship Troopers |
The Number of the Beast |
Book Titles |
Book Titles |
Return from the Stars |
Solaris |
Book Titles |
The Fellowship of the Ring |
The Hobbit |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
id | first | last |
7 | Douglas | Adams |
1 | Isaac | Asimov |
6 | Roald | Dahl |
4 | Charles | Dickens |
2 | Robert | Heinlein |
8 | mark56 | iUTMUATUqCOKzq |
3 | Stanisław | Lem |
5 | J. R. R. | Tolkien |
id | authorId | title |
9 | 4 | David Copperfield |
2 | 1 | Foundation |
1 | 1 | Foundation & Empire |
14 | 1 | I, Robot |
20 | 6 | James and the Giant Peach |
11 | 4 | Nicholas Nickleby |
13 | 4 | Oliver Twist |
34 | 3 | Return from the Stars |
5 | 1 | Second Foundation |
31 | 7 | So Long, and Thanks for All the Fish |
4 | 3 | Solaris |
3 | 2 | Starship Troopers |
23 | 6 | Tales of the Unexpected |
24 | 5 | The Fellowship of the Ring |
28 | 7 | The Hitchhiker's Guide to the Galaxy |
25 | 5 | The Hobbit |
15 | 2 | The Number of the Beast |
id | first | last | authorId | title |
9 | Charles | Dickens | 4 | David Copperfield |
11 | Charles | Dickens | 4 | Nicholas Nickleby |
13 | Charles | Dickens | 4 | Oliver Twist |
31 | Douglas | Adams | 7 | So Long, and Thanks for All the Fish |
28 | Douglas | Adams | 7 | The Hitchhiker's Guide to the Galaxy |
2 | Isaac | Asimov | 1 | Foundation |
1 | Isaac | Asimov | 1 | Foundation & Empire |
14 | Isaac | Asimov | 1 | I, Robot |
5 | Isaac | Asimov | 1 | Second Foundation |
24 | J. R. R. | Tolkien | 5 | The Fellowship of the Ring |
25 | J. R. R. | Tolkien | 5 | The Hobbit |
20 | Roald | Dahl | 6 | James and the Giant Peach |
23 | Roald | Dahl | 6 | Tales of the Unexpected |
3 | Robert | Heinlein | 2 | Starship Troopers |
15 | Robert | Heinlein | 2 | The Number of the Beast |
34 | Stanisław | Lem | 3 | Return from the Stars |
4 | Stanisław | Lem | 3 | Solaris |
first | last | title |
Isaac | Asimov | Foundation |
Isaac | Asimov | Foundation & Empire |
Isaac | Asimov | Second Foundation |
id | first | last | authorId | title |
9 | Charles | Dickens | 4 | David Copperfield |
2 | Charles | Dickens | 1 | Foundation |
1 | Charles | Dickens | 1 | Foundation & Empire |
14 | Charles | Dickens | 1 | I, Robot |
20 | Charles | Dickens | 6 | James and the Giant Peach |
11 | Charles | Dickens | 4 | Nicholas Nickleby |
13 | Charles | Dickens | 4 | Oliver Twist |
34 | Charles | Dickens | 3 | Return from the Stars |
5 | Charles | Dickens | 1 | Second Foundation |
31 | Charles | Dickens | 7 | So Long, and Thanks for All the Fish |
4 | Charles | Dickens | 3 | Solaris |
3 | Charles | Dickens | 2 | Starship Troopers |
23 | Charles | Dickens | 6 | Tales of the Unexpected |
24 | Charles | Dickens | 5 | The Fellowship of the Ring |
28 | Charles | Dickens | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Charles | Dickens | 5 | The Hobbit |
15 | Charles | Dickens | 2 | The Number of the Beast |
9 | Douglas | Adams | 4 | David Copperfield |
2 | Douglas | Adams | 1 | Foundation |
1 | Douglas | Adams | 1 | Foundation & Empire |
14 | Douglas | Adams | 1 | I, Robot |
20 | Douglas | Adams | 6 | James and the Giant Peach |
11 | Douglas | Adams | 4 | Nicholas Nickleby |
13 | Douglas | Adams | 4 | Oliver Twist |
34 | Douglas | Adams | 3 | Return from the Stars |
5 | Douglas | Adams | 1 | Second Foundation |
31 | Douglas | Adams | 7 | So Long, and Thanks for All the Fish |
4 | Douglas | Adams | 3 | Solaris |
3 | Douglas | Adams | 2 | Starship Troopers |
23 | Douglas | Adams | 6 | Tales of the Unexpected |
24 | Douglas | Adams | 5 | The Fellowship of the Ring |
28 | Douglas | Adams | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Douglas | Adams | 5 | The Hobbit |
15 | Douglas | Adams | 2 | The Number of the Beast |
9 | Isaac | Asimov | 4 | David Copperfield |
2 | Isaac | Asimov | 1 | Foundation |
1 | Isaac | Asimov | 1 | Foundation & Empire |
14 | Isaac | Asimov | 1 | I, Robot |
20 | Isaac | Asimov | 6 | James and the Giant Peach |
11 | Isaac | Asimov | 4 | Nicholas Nickleby |
13 | Isaac | Asimov | 4 | Oliver Twist |
34 | Isaac | Asimov | 3 | Return from the Stars |
5 | Isaac | Asimov | 1 | Second Foundation |
31 | Isaac | Asimov | 7 | So Long, and Thanks for All the Fish |
4 | Isaac | Asimov | 3 | Solaris |
3 | Isaac | Asimov | 2 | Starship Troopers |
23 | Isaac | Asimov | 6 | Tales of the Unexpected |
24 | Isaac | Asimov | 5 | The Fellowship of the Ring |
28 | Isaac | Asimov | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Isaac | Asimov | 5 | The Hobbit |
15 | Isaac | Asimov | 2 | The Number of the Beast |
9 | J. R. R. | Tolkien | 4 | David Copperfield |
2 | J. R. R. | Tolkien | 1 | Foundation |
1 | J. R. R. | Tolkien | 1 | Foundation & Empire |
14 | J. R. R. | Tolkien | 1 | I, Robot |
20 | J. R. R. | Tolkien | 6 | James and the Giant Peach |
11 | J. R. R. | Tolkien | 4 | Nicholas Nickleby |
13 | J. R. R. | Tolkien | 4 | Oliver Twist |
34 | J. R. R. | Tolkien | 3 | Return from the Stars |
5 | J. R. R. | Tolkien | 1 | Second Foundation |
31 | J. R. R. | Tolkien | 7 | So Long, and Thanks for All the Fish |
4 | J. R. R. | Tolkien | 3 | Solaris |
3 | J. R. R. | Tolkien | 2 | Starship Troopers |
23 | J. R. R. | Tolkien | 6 | Tales of the Unexpected |
24 | J. R. R. | Tolkien | 5 | The Fellowship of the Ring |
28 | J. R. R. | Tolkien | 7 | The Hitchhiker's Guide to the Galaxy |
25 | J. R. R. | Tolkien | 5 | The Hobbit |
15 | J. R. R. | Tolkien | 2 | The Number of the Beast |
9 | mark56 | iUTMUATUqCOKzq | 4 | David Copperfield |
2 | mark56 | iUTMUATUqCOKzq | 1 | Foundation |
1 | mark56 | iUTMUATUqCOKzq | 1 | Foundation & Empire |
14 | mark56 | iUTMUATUqCOKzq | 1 | I, Robot |
20 | mark56 | iUTMUATUqCOKzq | 6 | James and the Giant Peach |
11 | mark56 | iUTMUATUqCOKzq | 4 | Nicholas Nickleby |
13 | mark56 | iUTMUATUqCOKzq | 4 | Oliver Twist |
34 | mark56 | iUTMUATUqCOKzq | 3 | Return from the Stars |
5 | mark56 | iUTMUATUqCOKzq | 1 | Second Foundation |
31 | mark56 | iUTMUATUqCOKzq | 7 | So Long, and Thanks for All the Fish |
4 | mark56 | iUTMUATUqCOKzq | 3 | Solaris |
3 | mark56 | iUTMUATUqCOKzq | 2 | Starship Troopers |
23 | mark56 | iUTMUATUqCOKzq | 6 | Tales of the Unexpected |
24 | mark56 | iUTMUATUqCOKzq | 5 | The Fellowship of the Ring |
28 | mark56 | iUTMUATUqCOKzq | 7 | The Hitchhiker's Guide to the Galaxy |
25 | mark56 | iUTMUATUqCOKzq | 5 | The Hobbit |
15 | mark56 | iUTMUATUqCOKzq | 2 | The Number of the Beast |
9 | Roald | Dahl | 4 | David Copperfield |
2 | Roald | Dahl | 1 | Foundation |
1 | Roald | Dahl | 1 | Foundation & Empire |
14 | Roald | Dahl | 1 | I, Robot |
20 | Roald | Dahl | 6 | James and the Giant Peach |
11 | Roald | Dahl | 4 | Nicholas Nickleby |
13 | Roald | Dahl | 4 | Oliver Twist |
34 | Roald | Dahl | 3 | Return from the Stars |
5 | Roald | Dahl | 1 | Second Foundation |
31 | Roald | Dahl | 7 | So Long, and Thanks for All the Fish |
4 | Roald | Dahl | 3 | Solaris |
3 | Roald | Dahl | 2 | Starship Troopers |
23 | Roald | Dahl | 6 | Tales of the Unexpected |
24 | Roald | Dahl | 5 | The Fellowship of the Ring |
28 | Roald | Dahl | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Roald | Dahl | 5 | The Hobbit |
15 | Roald | Dahl | 2 | The Number of the Beast |
9 | Robert | Heinlein | 4 | David Copperfield |
2 | Robert | Heinlein | 1 | Foundation |
1 | Robert | Heinlein | 1 | Foundation & Empire |
14 | Robert | Heinlein | 1 | I, Robot |
20 | Robert | Heinlein | 6 | James and the Giant Peach |
11 | Robert | Heinlein | 4 | Nicholas Nickleby |
13 | Robert | Heinlein | 4 | Oliver Twist |
34 | Robert | Heinlein | 3 | Return from the Stars |
5 | Robert | Heinlein | 1 | Second Foundation |
31 | Robert | Heinlein | 7 | So Long, and Thanks for All the Fish |
4 | Robert | Heinlein | 3 | Solaris |
3 | Robert | Heinlein | 2 | Starship Troopers |
23 | Robert | Heinlein | 6 | Tales of the Unexpected |
24 | Robert | Heinlein | 5 | The Fellowship of the Ring |
28 | Robert | Heinlein | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Robert | Heinlein | 5 | The Hobbit |
15 | Robert | Heinlein | 2 | The Number of the Beast |
9 | Stanisław | Lem | 4 | David Copperfield |
2 | Stanisław | Lem | 1 | Foundation |
1 | Stanisław | Lem | 1 | Foundation & Empire |
14 | Stanisław | Lem | 1 | I, Robot |
20 | Stanisław | Lem | 6 | James and the Giant Peach |
11 | Stanisław | Lem | 4 | Nicholas Nickleby |
13 | Stanisław | Lem | 4 | Oliver Twist |
34 | Stanisław | Lem | 3 | Return from the Stars |
5 | Stanisław | Lem | 1 | Second Foundation |
31 | Stanisław | Lem | 7 | So Long, and Thanks for All the Fish |
4 | Stanisław | Lem | 3 | Solaris |
3 | Stanisław | Lem | 2 | Starship Troopers |
23 | Stanisław | Lem | 6 | Tales of the Unexpected |
24 | Stanisław | Lem | 5 | The Fellowship of the Ring |
28 | Stanisław | Lem | 7 | The Hitchhiker's Guide to the Galaxy |
25 | Stanisław | Lem | 5 | The Hobbit |
15 | Stanisław | Lem | 2 | The Number of the Beast |
<?php
/*------------------------------------------------------------*/
class Joins extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$queries = array(
array(
'title' => "Authors",
'sql' => "select * from authors order by last, first",
'exportFileName' => "Authors",
),
array(
'title' => "Books",
'sql' => "select * from books order by title",
'exportFileName' => "Books",
),
array(
'title' => "Join",
'sql' => "select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title",
'exportFileName' => "BooksAndAuthors",
),
array(
'title' => "Foundation",
'sql' => "select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title",
'exportFileName' => "Foundation",
),
array(
'title' => "The Complete Cartesian Product",
'sql' => "select a.*, b.* from authors a, books b order by a.first, a.last, b.title",
'exportFileName' => "Cartesian",
),
);
foreach ( $queries as $query ) {
Mview::msg($query['title'], true);
Mview::msg($query['sql']);
$this->showRows($query['sql'], true, $query['exportFileName']);
echo "<br /><br />\n";
}
$file = "Joins.class.php";
Mview::msg($file);
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
Sunday | Monday | Tuesday | Wednsday | Thursday | Friday | Saturday |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
<?php
/*------------------------------------------------------------*/
/*
/*------------------------------------------------------------*/
session_start();
/*------------------------------------------------------------*/
require_once("mdemoConfig.php");
require_once(M_DIR."/mfiles.php");
require_once("Mdemo.class.php");
/*------------------------------------------------------------*/
date_default_timezone_set("Asia/Jerusalem");
/*------------------------------------------------------------*/
global $Mview;
global $Mmodel;
$Mview = new Mview;
$Mview->assign("M", M_URL);
$Mmodel = new Mmodel;
/*------------------------------------------------------------*/
/**
* Mdemo extends Mcontroller and so dispatches URLs from here
* use PATH_INFO=/className/action/otherparts for mod rewrite pretty URLs or just ?className=...&action=...&otherargs
* Mcontroller extended $this->pathParts() returns array of argumnets to pretty URLs
*/
$Mdemo = new Mdemo;
$Mdemo->control();
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
class Mdemo extends Mcontroller {
/*------------------------------------------------------------*/
/**
* allow Mcontroller to do all the URL dispatching
* (this controller is called from index.php)
/*------------------------------------------------------------*/
/**
* if the url has no action specified, than we are just starting.
* show the frame page with the header, footer and the jquery UI tab center setup.
*/
public function index() {
$this->Mview->showTpl("mdemo.tpl", array(
"M" => M_URL,
));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<!--
display all header and footer information
and let jquery UI tabs Ajax everything else in the center without ever refreshing
-->
{msuShowTpl file="mdemoHead.tpl"}
{msuShowTpl file="mdemoHeader.tpl"}
<div style="font-size:70%;" class="tabs">
<ul>
<li><a class="noHijax tabLabel" href="?className=Authors&action=listAuthors"><span>Authors & Books</span></a></li>
<li><a class="noHijax tabLabel" href="?className=TicTacToe"><span>Tic Tac Toe</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Joins"><span>Join Tutorial</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Cal"><span>Calendar</span></a></li>
<li><a class="noHijax tabLabel" href="?className=ShowSource"><span>See the Source Code</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Mview&action=showTpl&tpl=admin.tpl"><span>Admin</span></a></li>
</ul>
</div>
{msuShowTpl file="mdemoFooter.tpl"}
{msuShowTpl file="mdemoFoot.tpl"}
<?php
/*------------------------------------------------------------*/
class Authors extends Mtable {
/*------------------------------------------------------------*/
/**
* an M scaffold is and extension of the class Mtable which in turn extends Mcontroller
* tell it the name of the table and the default sort order
*/
public function __construct() {
parent::__construct("authors", "last, first");
}
/*------------------------------------------------------------*/
public function listAuthors() {
/**
* use Mmodel to get the data from the database
* and pass the data to Mview to send the ajaxed content
* back to the browser for display
*
*/
$this->Mview->showTpl("authorsList.tpl", array(
"authors" => $this->Mmodel->getRows("select * from authors order by last,first"),
));
}
/*------------------------------------------------------------*/
public function listBooks() {
/**
* more the same
*/
$authorId = $_REQUEST['authorId'];
$this->Mview->showTpl("bookList.tpl", array(
"books" => $this->Mmodel->getRows("select * from books where authorId = $authorId order by title"),
));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
define('YOU', 'X');
define('ME', 'O');
/*------------------------------------------------------------*/
class TicTacToe extends Mcontroller {
/*------------------------------------------------------------*/
private $game;
/*------------------------------------------------------------*/
/**
* the default action is to start a new game.
*
* (this method is called by Mcontroller if no action is
* specified in the url)
*/
public function index() {
$this->newGame();
}
/*------------------------------------------------------------*/
/**
* to start a new game
* empty or re-empty the game array
* by placing null content in all the cells
*/
public function newGame() {
$this->game = array(
array(null, null, null,),
array(null, null, null,),
array(null, null, null,),
);
$_SESSION['game'] = $this->game;
$this->show();
}
/*------------------------------------------------------------*/
/**
* a player's move:
* place the player's chip in the requested spot
* and make the next move
*/
public function play() {
$this->game = $_SESSION['game'];
$x = $_REQUEST['x'];
$y = $_REQUEST['y'];
$this->game[$x][$y] = YOU;
$this->next();
}
/*------------------------------------------------------------*/
/**
* show the source code
*/
public function source() {
$this->menu();
$files = array(
"TicTacToe.class.php",
"tpl/ticTacToe.menu.tpl",
"tpl/ticTacToe.tpl",
);
foreach ( $files as $file ) {
$this->Mview->msg($file);
highlight_file($file);
}
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/**
* show the board
* if the game is over then game buttons are deactivated
*/
private function show($gameOver = false) {
$this->menu();
$this->Mview->showTpl("ticTacToe.tpl", array(
'game' => $this->game,
'active' => ! $gameOver,
));
}
/*------------------------------------------------------------*/
/**
* put on screen a menu of controls, if its not there already
*/
private function menu() {
static $vistied = false;
if ( $vistied )
return;
$vistied = true;
$this->Mview->showTpl("ticTacToe.menu.tpl");
}
/*------------------------------------------------------------*/
/**
* what to do next
* after the player played.
* check to see if the game is over.
* if it is, tell win/lose result and show
* the final board, with buttons deactivated.
* otherwise, make the move, and again check if the game is over.
*/
private function next() {
$this->menu();
if ( $this->isWinner(YOU)) {
$this->Mview->msg("Contratz. You win. Wanna Play Again?");
$this->show(true);
return;
}
if ( $this->isTie() ) {
$this->Mview->msg("Tie. Wanna Play Again?");
$this->show(true);
return;
}
$this->move();
if ( $this->isWinner(ME)) {
$this->Mview->msg(":-( Wanna Play Again?");
$this->show(true);
return;
}
$_SESSION['game'] = $this->game;
$this->show();
}
/*------------------------------------------------------------*/
/**
* is the given player ('ME' or 'YOU') a winner on the current state of the board
* check each of the rows, columns, and diagonals
*/
private function isWinner($who) {
$m = $this->game;
for($i=0;$i<3;$i++) {
// rows
if ( $this->trioWins($m[$i], $who) )
return(true);
// columns
if ( $this->trioWins(Mutils::arrayColumn($m, $i), $who) )
return(true);
}
// top-left to bottom-right
if ( $this->trioWins(array($m[0][0], $m[1][1], $m[2][2],), $who) )
return(true);
// top-right to bottom-left
if ( $this->trioWins(array($m[0][2], $m[1][1], $m[2][0],), $who) )
return(true);
return(false);
}
/*------------------------------------------------------------*/
/**
* does this array of three values represent a winning for the given player
* it does if all three values equal the argument
*/
private function trioWins($trio, $who) {
for($i=0;$i<3;$i++)
if ( $trio[$i] != $who )
return(false);
return(true);
}
/*------------------------------------------------------------*/
/**
* check if the board represents a tie.
* in fact, do not tell its a tie if there are still moves left
* (so they can be played anyway, even if to futility),
* and since this method is only called after winning combinations were checked,
* it boils down to merely checking to see that all places are filled with chips.
*/
private function isTie() {
$m = $this->game;
for($x=0;$x<3;$x++)
for($y=0;$y<3;$y++)
if ( $m[$x][$y] == null )
return(false);
return(true);
}
/*------------------------------------------------------------*/
/**
* make a move
* this is the game strategy
* first try to see if a win is possible
* otherwise, block any potential win by the player
* otherwise, see if you can place a chip in the center
* otherwise, see if you can place a chip in a corner
* otherwise, see if you can place a chip on a side
* all the play functions return true if a chip was placed and false otherwise
*/
private function move() {
$moveFuncs = array('win', 'block', 'center', 'corner', 'side');
foreach ( $moveFuncs as $func )
if ( $this->$func() )
return;
}
/*------------------------------------------------------------*/
/**
* try to win by completing a trio with the value ME
*/
private function win() {
return($this->complete(ME));
}
/*------------------------------------------------------------*/
/**
* try to block the player by completing the players trio
* in place of the player.
*/
private function block() {
return($this->complete(YOU));
}
/*------------------------------------------------------------*/
/**
* try to complete a trio that already has two chips of the same kind
* if the chips are 'ME', then this is a win attempt
* if the chips are 'YOU' then this is a block attempt
* since if there are two block cases, the game is lost anyway
* it is not important which is being blocked
* the method completeTrio() is called successively with all potential possibilties
* until a completion is found
* in which case the chip is placed in the designated place
* completeTrio is passed a trio in each case
* and the exact position is 'calculated' from the return value
*/
private function complete($who) {
$m = $this->game;
for($i=0;$i<3;$i++) {
// rows
if ( ($idx = $this->completeTrio($m[$i], $who)) >= 0 ) {
$this->game[$i][$idx] = ME;
return(true);
}
// columns
if ( ($idx = $this->completeTrio(Mutils::arrayColumn($m, $i), $who)) >= 0 ) {
$this->game[$idx][$i] = ME;
return(true);
}
}
// top-left to bottom-right
if ( ($idx = $this->completeTrio(array($m[0][0], $m[1][1], $m[2][2],), $who)) >= 0 ) {
$this->game[$idx][$idx] = ME;
return(true);
}
// top-right to bottom-left
if ( ($idx = $this->completeTrio(array($m[0][2], $m[1][1], $m[2][0],), $who)) >= 0 ) {
$this->game[$idx][2-$idx] = ME;
return(true);
}
return(false);
}
/*------------------------------------------------------------*/
/**
* given an array of three values
* tell if it can be completed by placing a chip on the only vacant position.
* return the position (0-2) if so, or -1 if not
*/
private function completeTrio($trio, $who) {
$numComplete = 0;
$ret = null;
for($i=0;$i<3;$i++) {
if ( $trio[$i] != null && $trio[$i] != $who )
return(-1);
if ( $trio[$i] == $who )
$numComplete++;
else
$ret = $i;
}
if ( $numComplete == 2 )
return($ret);
return(-1);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in the center of the baord
*/
private function center() {
return($this->place(1, 1));
}
/*------------------------------------------------------------*/
/**
* try to place a chip in the given position
*/
private function place($x, $y) {
if ( $this->game[$x][$y] != null )
return(false);
$this->game[$x][$y] = ME ;
return(true);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in any corner
* randomize so that the move is a bit less predictable
*/
private function corner() {
$corners = array(
array(0,0),
array(0,2),
array(2,0),
array(2,2),
);
return($this->placeAtRandom($corners));
}
/*------------------------------------------------------------*/
/**
* place in any of the given list of places at random
*/
private function placeAtRandom($places) {
shuffle($places);
foreach ( $places as $place )
if ( $this->place($place[0], $place[1]) )
return(true);
return(false);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in any of the sides
*/
private function side() {
$sides = array(
array(0,1),
array(1,0),
array(1,2),
array(2,1),
);
return($this->placeAtRandom($sides));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
class Joins extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$queries = array(
array(
'title' => "Authors",
'sql' => "select * from authors order by last, first",
'exportFileName' => "Authors",
),
array(
'title' => "Books",
'sql' => "select * from books order by title",
'exportFileName' => "Books",
),
array(
'title' => "Join",
'sql' => "select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title",
'exportFileName' => "BooksAndAuthors",
),
array(
'title' => "Foundation",
'sql' => "select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title",
'exportFileName' => "Foundation",
),
array(
'title' => "The Complete Cartesian Product",
'sql' => "select a.*, b.* from authors a, books b order by a.first, a.last, b.title",
'exportFileName' => "Cartesian",
),
);
foreach ( $queries as $query ) {
Mview::msg($query['title'], true);
Mview::msg($query['sql']);
$this->showRows($query['sql'], true, $query['exportFileName']);
echo "<br /><br />\n";
}
$file = "Joins.class.php";
Mview::msg($file);
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
class Cal extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$this->showCal(date("Y"), date("n"));
}
/*------------------------------------------------------------*/
public function showCal($year = null, $month = null) {
if ( ! $year )
$year = $_REQUEST['year'];
if ( ! $month )
$month = $_REQUEST['month'];
$cal = Mdate::cal($year, $month);
$this->menu($year, $month);
$this->Mview->showTpl("cal.tpl", array(
'cal' => $cal,
'today' => ( date("Y") == $year && date("n") == $month ) ? date("d") : null,
));
}
/*------------------------------------------------------------*/
private function menu($year = null, $month = null) {
if ( $year == null )
$year = date("Y");
if ( $month == null )
$month = date("m");
$this->Mview->showTpl("cal.menu.tpl", array(
'year' => $year,
'month' => $month,
'months' => Mdate::monthLlist(),
));
}
/*------------------------------------------------------------*/
public function nextYear() {
$this->showCal($_REQUEST['year']+1, $_REQUEST['month']);
}
/*------------------------------------------------------------*/
public function prevYear() {
$this->showCal($_REQUEST['year']-1, $_REQUEST['month']);
}
/*------------------------------------------------------------*/
public function nextMonth() {
$this->showCal($_REQUEST['year'] + (($_REQUEST['month'] == 12) ? 1 : 0), ($_REQUEST['month'])%12+1);
}
/*------------------------------------------------------------*/
public function prevMonth() {
$this->showCal($_REQUEST['year'] - (($_REQUEST['month'] == 1) ? 1 : 0), ($_REQUEST['month']+10)%12+1);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
class ShowSource extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$this->menu();
}
/*------------------------------------------------------------*/
public function fileList() {
$files = array(
"index.php",
"Mdemo.class.php",
"tpl/mdemo.tpl",
"Authors.class.php",
"TicTacToe.class.php",
"Joins.class.php",
"Cal.class.php",
"ShowSource.class.php",
"../M/Mmodel.class.php",
"../M/Mview.class.php",
"../M/Mcontroller.class.php",
);
return($files);
}
/*------------------------------------------------------------*/
public function menu() {
$this->Mview->showTpl("showSource.menu.tpl", array(
'files' => $this->fileList(),
));
}
/*------------------------------------------------------------*/
public function showFile($file = null) {
if ( ! $file ) {
$fileId = $_REQUEST['fileId'];
$files = $this->fileList();
$file = $files[$fileId];
}
echo "<h4>$file</h4>\n";
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
/**
* Mmodel - mysql convenience utilities
*
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* Mmodel may be used independently,
* It might use Mview to display some error messages
*/
require_once("Mview.class.php");
/*------------------------------------------------------------*/
/**
* Mmodel - mysql convenience utilities
*
* @package M
* @author Ohad Aloni
*/
class Mmodel {
/*------------------------------------------------------------*/
private $isConnected = false;
/*------------------------------------------------------------*/
private $dbHost = null;
private $dbUser = null;
private $dbPasswd = null;
private $dbName = null;
/*------------------------------*/
private $dbHandle = null;
private $lastSql = null;
private $lastError = null;
private $lastInsertId = null;
/*------------------------------*/
private $Mmemcache = null;
/*------------------------------------------------------------*/
public function __construct($user = null, $passwd = null, $dbName = null, $host = null) {
if ( $host )
$this->dbHost = $host;
elseif ( defined('M_HOST') )
$this->dbHost = M_HOST;
else
$this->dbHost = 'localhost';
if ( $dbName )
$this->dbName = $dbName;
elseif ( defined('M_DBNAME') && M_DBNAME != 'none' )
$this->dbName = M_DBNAME;
else
$this->dbName = null;
if ( $user )
$this->dbUser = $user;
elseif ( defined('M_USER') )
$this->dbUser = M_USER;
else
$this->dbUser = null;
if ( $passwd )
$this->dbPasswd = $passwd;
elseif ( defined('M_PASSWORD') )
$this->dbPasswd = M_PASSWORD;
else
$this->dbPasswd = null;
if ( ! $this->dbUser || $this->dbPasswd === null ) {
Mview::error("Must define M_USER M_PASSWORD or construct Mmodel with arguments");
return;
}
if ( ! $this->selectHost($this->dbHost, $this->dbUser, $this->dbPasswd, $this->dbName) )
return;
$this->isConnected = true;
}
/*------------------------------*/
public function isConnected() {
return($this->isConnected);
}
/*------------------------------------------------------------*/
public function selectHost($dbHost, $dbUser, $dbPasswd, $dbName) {
$this->dbHost = $dbHost;
$this->dbUser = $dbUser;
$this->dbPasswd = $dbName;
$this->dbName = $dbName;
$this->dbHandle = @mysqli_connect($dbHost, $dbUser, $dbPasswd);
if ( ! $this->dbHandle ) {
$error = "Cannot connect to DB on $dbHost";
$this->lastError = $error;
Mview::error($error);
return(false);
}
$res = $this->query("SET NAMES 'utf8'");
if ( ! $res ) {
Mview::error("cannot set names to utf8");
return(false);
}
if ( $dbName ) {
if ( ! $this->selectDb($dbName) ) {
$error = @mysqli_error($this->dbHandle) ;
$this->lastError = $error;
Mview::error("Unable to select db $dbName: $error");
}
}
return(true);
}
/*------------------------------------------------------------*/
/**
* use database
*/
public function selectDB($db) {
$this->dbName = $db;
$ret = @mysqli_select_db($this->dbHandle, $this->dbName);
if ( $ret == false ) {
$error = @mysqli_error($this->dbHandle) ;
$this->lastError = $error;
return(false);
}
return(true);
}
/*------------------------------*/
/**
* use database
*/
public function useDB($db) {
return($this->selectDB($db));
}
/*------------------------------------------------------------*/
public function insertId() {
return($this->lastInsertId);
}
/*------------------------------------------------------------*/
/**
* make a string usable in quotes of sql statement
* @param string
* @return string
*/
public function str($str) {
if ( ! $str )
return($str);
$ret = $str;
$ret = str_replace("\\", "\\\\", $ret);
// if they are already escaped
$ret = str_replace("\\'", "'", $ret);
$ret = str_replace("'", "\\'", $ret);
$ret = str_replace("\r\n", "\n", $ret);
$ret = str_replace("\n", "\\n", $ret);
return($ret);
}
/*------------------------------------------------------------*/
public function query($sql) {
$res = null;
try {
$res = @mysqli_query($this->dbHandle, $sql);
} catch (Exception $e) {
$msg = $e->getMessage();
Mview::error($msg);
$error = @mysqli_error($this->dbHandle);
$this->lastError = $error;
if ( $error )
Mview::error("sql error: $error");
Mview::error($sql);
return(null);
}
if ( ! $res ) {
$error = @mysqli_error($this->dbHandle);
$this->lastError = $error;
if ( $error )
Mview::error("sql error: $error");
Mview::error(substr($sql, 0, 500));
if ( stristr($error, "MySQL server has gone away") ) {
Mview::error("EXITTING");
exit; // servers will auto restart
}
return(null);
}
return($res);
}
/*------------------------------------------------------------*/
public function _sql($sql, $rememberLastSql = true) {
$res = $this->query($sql);
if ( ! $res ) {
return(null);
}
if ( $rememberLastSql )
$this->lastSql = $sql;
$affected = @mysqli_affected_rows($this->dbHandle);
@mysqli_free_result($res);
return($affected);
}
/*------------------------------*/
public function sql($sql) {
if ( ! $this->isConnected ) {
return(null);
}
$ret = $this->_sql($sql);
if ( strstr($sql, 'insert') )
$this->lastInsertId = @mysqli_insert_id($this->dbHandle);
if ( $ret > 0 )
$this->dbLog('', 'sql', 0);
return($ret);
}
/*----------------------------------------*/
public function getRows($sql, $ttl = null) {
if ( $ttl !== null && ! $this->Mmemcache )
$this->Mmemcache = new Mmemcache;
$memcacheKey = $this->memcacheKey($sql);
if ( $ttl !== null && ($rows = $this->Mmemcache->get($memcacheKey)) !== false ) {
return($rows);
}
if ( ! $this->isConnected ) {
return(null);
}
$res = $this->query($sql);
if ( ! $res ) {
return(null);
}
$ret = array();
while($r = @mysqli_fetch_assoc($res))
$ret[] = $r ;
@mysqli_free_result($res);
if ( $ttl !== null ) {
$set = $this->Mmemcache->set($memcacheKey, $ret, $ttl);
$debugLevel = Mutils::getenv("debugLevel");
if ( ! $set && $debugLevel )
echo "Failed to Mmemcache->set($memcacheKey, ..., $ttl)<br />\n";
if ( $debugLevel ) {
static $visited = false;
if ( ! $visited ) {
$visited = true;
$get = $this->Mmemcache->get($memcacheKey);
if ( $get === false ) {
echo "Failed to get after Mmemcache->set($memcacheKey, ..., $ttl)<br />\n";
$this->memcacheTestData($ret, $memcacheKey, $ttl);
}
}
}
}
return($ret);
}
/*----------*/
private function memcacheKey($sql) {
$codeVersion = 9;
$dbHost = $this->dbHost;
$dbName = $this->dbName;
$memcacheKey = "$codeVersion-$dbHost-$dbName-$sql";
return($memcacheKey);
}
/*--------------------*/
private function memcacheTestData($data, $key, $ttl) {
$set = $this->Mmemcache->set($key, $data, $ttl);
$get = $this->Mmemcache->get($key);
$memcacheTestResults = array(
'key' => $key,
'data' => $data,
'ttl' => $ttl,
'set' => $set,
'get' => $get,
);
Mview::print_r($memcacheTestResults, "memcacheTestResults", basename(__FILE__), __LINE__);
return($set);
}
/*------------------------------------------------------------*/
public function getRow($sql, $ttl = null) {
if ( ! $this->isConnected ) {
return(null);
}
$rows = $this->getRows($sql, $ttl);
if ( count($rows) == 0 )
return(null);
return($rows[0]);
}
/*------------------------------*/
/**
* get a row from a table by its id
*
* @param string the table from which the data is fetched
* @param int the id value
* @param string the name of the id field if it is not 'id'
* @return array associative array of data of the row
*/
public function getById($tableName, $id, $ttl = null, $idName = "id") {
return($this->getRow("select * from $tableName where $idName = $id", $ttl));
}
/*----------------------------------------*/
/**
* get a column of data from the database
*
* @param string the query
* @return array an array with the data
*/
public function getStrings($sql, $ttl = null) {
$rows = $this->getRows($sql, $ttl);
if ( $rows === null )
return(null);
$ret = array();
foreach ( $rows as $row )
$ret[] = array_shift($row); // take the value of the first (and only) column, ignoring the index field name
return($ret);
}
/*----------------------------------------*/
/**
* get an item (single row, single column) from the database
*
* @param string the query
* @return string the item data
*/
public function getString($sql, $ttl = null) {
if ( ! $this->isConnected ) {
return(null);
}
$strings = $this->getStrings($sql, $ttl);
if ( $strings )
return($strings[0]);
else
return(null);
}
/*----------------------------------------*/
/**
* get an int item from the database
*
* @param string the query
* @return int the returned number
*/
public function getInt($sql, $ttl = null) {
if ( ! $this->isConnected ) {
return(null);
}
if ( ($ret = $this->getString($sql, $ttl)) === null )
return(null);
return((int)$ret);
}
/*------------------------------------------------------------*/
/**
* name of the auto_increment column in table
*
* @param string the name of the table
* @return string name of auto_increment column
*/
public function autoIncrement($tableName) {
if ( ! $this->Mmemcache )
$this->Mmemcache = new Mmemcache;
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$memcacheKey = $this->memcacheKey("autoIncrement-$tableName");
if ( ($cache[$tableName] = $this->Mmemcache->get($memcacheKey)) != null )
return($cache[$tableName]);
$fields = $this->fields($tableName);
if ( ! $fields ) {
$cache[$tableName] = null;
return($cache[$tableName]);
}
foreach ( $fields as $field ) {
if ( $field['isAutoInc'] ) {
$cache[$tableName] = $field['name'];
break;
}
}
if ( ! isset($cache[$tableName]) )
$cache[$tableName] = null;
if ( $cache[$tableName] )
$this->Mmemcache->set($memcacheKey, $cache[$tableName], 600);
return($cache[$tableName]);
}
/*------------------------------------------------------------*/
public function typeGroup($ftype) {
if ( strncmp($ftype, 'int', 3) == 0 )
return("int");
if ( $ftype == 'text' )
return("text");
if ( strncmp($ftype, 'varchar', 7) == 0 )
return("text");
if ( in_array($ftype, array('date', 'datetime', 'timestamp')) )
return("date");
return($ftype);
}
/*------------------------------------------------------------*/
/**
* schema information for a table:
*
* @param string the name of the table
* @return array two dimensional array. For each column in the table: name, type, isNull, isPrimary, default, isAutoInc
*/
public function fields($tableName) {
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$columnRows = $this->getRows("show columns from $tableName", 30);
if ( ! $columnRows ) {
Mview::error("No columns for $tableName");
$cache[$tableName] = null ;
return($cache[$tableName]);
}
$fields = array();
foreach ( $columnRows as $col )
$fields[] = array(
'name' => $col['Field'],
'type' => $col['Type'],
'typeGroup' => $this->typeGroup($col['Type']),
'isNull' => $col['Null'] == 'YES' ? true : false,
'isPrimary' => $col['Key'] == 'PRI',
'default' => $col['Default'],
'isAutoInc' => $col['Extra'] == 'auto_increment',
'isKey' => $col['Key'] != null,
);
$cache[$tableName] = $fields ;
return($cache[$tableName]);
}
/*----------------------------------------*/
/**
* schema of one field
*/
public function field($tableName, $fieldName) {
$fields = $this->fields($tableName);
if ( ! $fields )
return(null);
foreach ( $fields as $field )
if ( $field['name'] == $fieldName )
return($field);
return(null);
}
/*----------------------------------------*/
/**
* list fields of a table
*
* @param string the name of the table
* @return array list of field names
*/
public function columns($tableName) {
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$columnRows = $this->getRows("show columns from $tableName", 30);
if ( ! $columnRows ) {
Mview::error("No columns for $tableName");
$cache[$tableName] = null ;
return($cache[$tableName]);
}
$cols = array();
foreach ( $columnRows as $col )
$cols[] = $col['Field'] ;
$cache[$tableName] = $cols ;
return($cache[$tableName]);
}
/*----------------------------------------*/
/**
* does field exist in a table
*
* @param string the name of the table
* @param string the name of the field
* @return bool
*/
public function isColumn($tableName, $column) {
$columns = $this->columns($tableName);
if ( ! $columns )
return(false);
return(in_array($column, $columns));
}
/*----------------------------------------*/
/**
* number of rows in a table
*
* @param string the name of the table
* @return int the number of rows in the table
*/
public function rowNum($t) {
return($this->getInt("select count(*) from $t"));
}
/*----------------------------------------*/
/**
* list of tables in database
*
* @param string the database name (optional - if not the default database)
* @return array list of tables
*/
public function tables($db = null) {
static $cache = null;
if ( ! $db )
$db = $this->dbName;
if ( ! $db )
return(false);
if ( isset($cache[$db]) )
return($cache[$db]);
$cache[$db] = $this->getStrings("show tables from $db");
return($cache[$db]);
}
/*----------------------------------------*/
/**
* list of databases
*/
public function databases() {
static $cache = null;
static $excludes = array('performance_schema', 'information_schema', 'mysql', 'test',);
$dbHost = $this->dbHost;
if ( isset($cache[$dbHost]) )
return($cache[$dbHost]);
$allDatabases = $this->getStrings("show databases");
$databases = array();
foreach ( $allDatabases as $db )
if ( ! in_array($db, $excludes) )
$databases[] = $db;
$cache[$dbHost] = $databases;
return($cache[$dbHost]);
}
/*----------------------------------------*/
/**
* does table exist
*
* @param string the table name
* @return bool
*/
public function isTable($t) {
$pair = explode('.', $t);
if ( count($pair) == 2 ) {
$dbname = $pair[0];
$tname = $pair[1];
$sql = "select count(*) from information_schema.tables where table_schema = '$dbname' and table_name = '$tname'";
return($this->getInt($sql));
}
$tables = $this->tables();
// with xampp, all table names are lowercase but $t might not be
return(in_array($t, $tables) || in_array(strtolower($t), $tables));
}
/*------------------------------------------------------------*/
/**
* dbInsert() without logging (see dbLog())
*
* @param string table to insert the data to
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @return int auto-increment id of the new row
*/
public function _dbInsert($tableName, $data, $rememberLastSql = true, $withId = false) {
if ( ! $this->isConnected ) {
return(null);
}
if ( ($sql = $this->dbInsertSql($tableName, $data, $withId)) == null )
return(null);
$affected = $this->_sql($sql, $rememberLastSql);
if ( $affected != 1 )
return(null);
$this->lastInsertId = mysqli_insert_id($this->dbHandle);
return($this->lastInsertId);
}
/*------------------------------*/
/**
* insert data to database
*
* @param string table to insert the data to
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @return int auto-increment id of the new row
*/
public function dbInsert($tableName, $data, $withId = false) {
if ( ($id = $this->_dbInsert($tableName, $data, true, $withId)) == null )
return(null);
$this->dbLog($tableName, 'insert', $id);
return($id);
}
/*------------------------------------------------------------*/
public function bulkInsert($tableName, $rows) {
$columns = $this->columns($tableName);
unset($columns[0]); // avoid the id field
$names = implode(", ", $columns);
$valuesLists = array();
foreach ( $rows as $row ) {
$values = array();
foreach ( $columns as $column ) {
if ( isset($row[$column]) ) {
$value = $row[$column];
$dbStr = $this->str($value);
$valueStr = "'$dbStr'";
} else {
$valueStr = "null";
}
$values[] = $valueStr;
}
$valuesString = implode(", ", $values);
$valuesLists[] = $valuesString;
}
$bulkValues = "( ".implode(" ), ( ", $valuesLists)." )";
$sql = "insert $tableName ( $names ) values $bulkValues";
$affected = $this->sql($sql);
return($affected);
}
/*------------------------------------------------------------*/
/**
* update a row of data - raw interface - see also dbUpdate() & dbLog()
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @param string the name of the id field if it is not 'id'
* @return int -1 on error, 0 if the query had no effect,
* 1 if an actual change occured
*/
public function _dbUpdate($tableName, $id, $data, $idName = "id") {
if ( ! $this->isConnected ) {
return(-1);
}
$cols = $this->columns($tableName);
if ( ! $cols )
return(-1);
$this->ammend($data, $cols);
$origData = $this->getRow("select * from $tableName where $idName = $id");
$pairs = array();
foreach ( $data as $fname => $value ) {
if ( $fname == $idName || ! in_array($fname, $cols) || $this->equiv($origData[$fname], $value) )
continue;
$dataType = $this->dataType($tableName, $fname);
if ( $dataType == 'timestamp' )
continue;
if ( $dataType == 'date' && $value != null && ($value = Mdate::scan($value)) == null )
continue;
if ( $dataType == 'datetime' && $value != null && ($value = Mdate::datetimeScan($value)) == null )
continue;
$str = $this->str($value);
if ( $str === 'now()' )
$pairs[] = "$fname = $str";
elseif ( $str === null )
$pairs[] = "$fname = null";
else
$pairs[] = "$fname = '$str'";
}
if ( ! $pairs ) {
/* $json = json_encode($data); */
/* Mview::msg("$tableName: nothing changed: $json"); */
// nothing changed - do nothing else
return(0);
}
$pairList = implode(", ", $pairs);
$sql = "update $tableName set $pairList where $idName = $id";
$affected = $this->_sql($sql);
return($affected);
}
/*--------------------*/
/**
* update a row of data
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @param string the name of the id field if it is not 'id'
* @return bool true if all is well, (including no-change), false on error
*/
public function dbUpdate($tableName, $id, $data, $idName = "id") {
$affected = $this->_dbUpdate($tableName, $id, $data, $idName);
if ( $affected > 0 )
$this->dbLog($tableName, 'update', $id);
return($affected);
}
/*----------------------------------------*/
/**
* delete a row (without logging - see dbLog())
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param string the name of the id field if it is not 'id'
* @return int 1 on success, 0 if nothing was deleted, -1 if an error occured
*/
public function _dbDelete($tableName, $id, $idName = "id") {
if ( ! $this->isConnected ) {
return(-1);
}
$sql = "delete from $tableName where $idName = $id";
$affected = $this->_sql($sql);
return($affected);
}
/*----------------------------------------*/
/**
* delete a row
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param string the name of the id field if it is not 'id'
* @return bool true if all is well, (including no-change), false on error
*/
public function dbDelete($tableName, $id, $idName = "id") {
$affected = $this->_dbDelete($tableName, $id, $idName);
if ( $affected > 0 )
$this->dbLog($tableName, 'delete', $id);
return($affected >= 0);
}
/*------------------------------------------------------------*/
/**
* return sql representing the data in the table ( use dumpTable() to stream large tables )
*
* @param string the table name
* @param string single field to sort by or null to avoid sorting
* @return string sql statement(s) to (re-)insert data to the table
*/
public function tableDump($tableName, $orderBy = "id") {
if ( $orderBy && $this->isColumn($tableName, $orderBy) )
$ob = "order by $orderBy";
else
$ob = "";
$ret = "drop table if exists $tableName;\n";
$cr = $this->getRow("show create table $tableName");
$crvalues = array_values($cr);
$ret .= $crvalues[1].";\n";
$rows = $this->getRows("select * from $tableName $ob");
foreach ( $rows as $row )
$ret .= $this->dbInsertSql($tableName, $row, true).";\n";
return($ret);
}
/*------------------------------------------------------------*/
/**
* stream an SQL dump of the table to the standard output
*
* @param string the table name
*/
public function dumpTable($tableName, $select = null) {
$ai = $this->autoIncrement($tableName);
if ( $ai )
$ob = "order by $ai";
else
$ob = "";
$datetime = date("Y-m-d G:i:s.u");
if ( $select ) {
$query = $select;
echo "-- M::dumpTable - $tableName - $select\n";
} else {
$query = "select * from $tableName $ob";
echo "-- M::dumpTable starting - $tableName - $datetime\n";
echo "drop table if exists $tableName;\n";
$cr = $this->getRow("show create table $tableName");
$crvalues = array_values($cr);
echo $crvalues[1].";\n";
}
$res = $this->query($query);
if ( ! $res )
return;
while($row = @mysqli_fetch_assoc($res))
echo $this->dbInsertSql($tableName, $row, true).";\n";
if ( ! $select ) {
$datetime = date("Y-m-d G:i:s.u");
echo "-- M::dumpTable done - $tableName - $datetime\n";
echo "\n";
}
}
/*------------------------------------------------------------*/
/**
* the data type of a column in a table
*
* @param string the table name
* @param string the column name
* @return string the data type
*/
public function dataType($tableName, $fieldName) {
static $cache = array();
if ( isset($cache[$tableName][$fieldName]) )
return($cache[$tableName][$fieldName]);
$columnRows = $this->getRows("show columns from $tableName", 2*3600);
foreach ( $columnRows as $col )
$cache[$tableName][$col['Field']] = $col['Type'];
if ( isset($cache[$tableName][$fieldName]) )
return($cache[$tableName][$fieldName]);
return(null);
}
/*------------------------------------------------------------*/
/**
* provide data for jquery autocomplete
*
* the request url is of the form id=$tname-$fname&q=...
* e.g.
* $(".autocomplete", context).autocomplete("?className=Mmodel&action=autocomplete&id=" + $this.id);
*/
public function autoComplete() {
$id = explode('-', @$_REQUEST['id']);
if ( ! $id ) {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete error\n";
return;
}
$cnt = count($id);
if ( $cnt == 2 )
list($tname, $fname) = $id;
elseif ( $cnt == 3 ) {
list($dbname, $tname, $fname) = $id;
if ( ! $this->selectDB($dbname) ) {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete: cannont use $dbname\n";
return;
}
} else {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete error\n";
return;
}
$q = $_REQUEST['q'];
$sql = "select distinct $fname from $tname where $fname like '$q%' order by $fname";
$strings = $this->getStrings($sql);
if ( ! $strings )
return;
$ret = implode("\n", $strings)."\n";
echo $ret;
}
/*------------------------------*/
public function permit() {
// allows automatic Mautocomplete!!
return(true);
}
/*------------------------------------------------------------*/
/**
* update a single item in the database from parameters in $_REQUEST
*
* saveFieldInfo() is used directly from the jQuery jeditable plugin
* as a complete server-side ajax updater/responder
*/
public function saveFieldInfo() {
$elementId = $_REQUEST['id'];
$value = $_REQUEST['value'];
$nameId = explode("-", $elementId);
$tname = $nameId[0];
$fname = $nameId[1];
$id = $nameId[2];
$OldValue = $this->getString("select $fname from $tname where id = $id");
// updating the database requires login credentials
if ( ! Mlogin::get('MloginName') ) {
echo "$OldValue";
exit;
}
$dataType = $this->dataType($tname, $fname);
if ( $dataType == 'date' ) {
if ( ($value = Mdate::scan($value)) == null ) {
echo "$OldValue";
exit;
}
}
if ( $dataType == 'time' ) {
if ( ($value = Mtime::fmt($value)) == null ) {
echo "$OldValue";
exit;
}
}
$sqlValue = $this->str($value);
$sql = "update $tname set $fname = '$sqlValue' where id = $id";
$affected = $this->sql($sql);
$newValue = $this->getString("select $fname from $tname where id = $id");
if ( $dataType == 'date' )
$newValue = Mdate::fmt($newValue);
elseif ( $dataType == 'time' )
$newValue = Mtime::fmt($newValue);
elseif ( $dataType == 'float' || $dataType == 'double' )
$newValue = msuFloatFmt((float)$newValue);
echo $newValue;
}
/*------------------------------------------------------------*/
/**
* log database activity in a table called queryLog
*
* Normally only used as 'private',
* dbInsert(), dbUpdate(), dbDelete() and sql() attempt
* to log activities when successful changes occur,
* while _dbInsert(), _dbUpdate(), _dbDelete() and _sql() do not.
*
* @param string table name
* @param operation performed
* @param int id of the affected row
* @param string the sql statement performing the operation
*/
public function dbLog($tname, $op, $tid, $querySql = null) {
if ( ! $this->isTable('queryLog') )
return;
$excludeTables = array(
'authLog',
'errorLog',
'online',
'tlog',
'timeWatch',
'usageStats',
'loads',
);
if ( in_array($tname, $excludeTables) )
return;
$querySql = $querySql ? $querySql : $this->lastSql;
$querySql = str_replace("\\'", "'", $querySql); // remove added escapes that will be added again by _dbInsert
foreach ( $excludeTables as $excludeTable ) {
if ( strstr($querySql, " $excludeTable ") )
return;
}
$row = array(
'tname' => $tname,
'op' => $op,
'tid' => $tid,
'querySql' => $querySql,
'loginName' => Mlogin::get('MloginName'),
'stamp' => date("Y-m-d H:i:s"),
);
$this->_dbInsert('queryLog', $row, false);
}
/*------------------------------------------------------------*/
/**
* a database ready representation of now()
*
* @return string
*/
public function datetimeNow() {
$today = Mdate::dash(Mdate::today());
$now = Mtime::fmt(Mtime::now());
$ret = "$today $now";
return($ret);
}
/*------------------------------*/
/**
* a database ready representation of now() in a given timezone
*
* @param string timezone (see date_default_timezone_set())
* @return string
*/
public function datetimeNowInTZ($tz = null) {
$todayInTZ = Mdate::dash(Mdate::todayInTZ($tz));
$nowInTZ = Mtime::nowInTZ($tz, true);
$ret = "$todayInTZ $nowInTZ";
return($ret);
}
/*------------------------------------------------------------*/
/**
* sql for a sample of rows from table
*
* @param string name of table
*/
public function sampleSql($table) {
if ( ! $this->isTable($table) )
return(null);
$id = $this->autoIncrement($table);
$fieldList = implode(',', $this->columns($table));
$rowNum = $this->rowNum($table);
if ( ! $id ) {
if ( $rowNum > 500 )
$limit = "limit 500";
else
$limit = "";
return("select $fieldList from $table $limit");
}
if ( $rowNum < 100 )
$sql = "select $fieldList from $table order by $id";
elseif ( $rowNum < 1000 )
$sql = "select $fieldList from $table where $id % 10 = 0 order by $id";
elseif ( $rowNum < 10000 )
$sql = "select $fieldList from $table where $id % 100 = 0 order by $id";
elseif ( $rowNum < 100000 )
$sql = "select $fieldList from $table where $id % 1000 = 0 order by $id";
else
$sql = "select $fieldList from $table order by $id desc limit 100";
return($sql);
}
/*------------------------------------------------------------*/
public function name($tname, $fname, $id) {
static $cache = array();
if ( isset($cache[$tname][$fname][$id]) )
return($cache[$tname][$fname][$id]);
if ( ! @$cache[$tname] )
$cache[$tname] = array();
if ( ! @$cache[$tname][$fname] ) {
$cache[$tname][$fname] = array();
$rows = $this->getRows("select id,$fname from $tname", 30*60);
foreach ( $rows as $row )
$cache[$tname][$fname][$row['id']] = $row[$fname];
}
return(@$cache[$tname][$fname][$id]);
}
/*------------------------------*/
public function id($tname, $fname, $name, $make = false) {
static $cache = array();
if ( isset($cache[$tname][$fname][$name]) )
return($cache[$tname][$fname][$name]);
if ( ! @$cache[$tname] )
$cache[$tname] = array();
if ( ! @$cache[$tname][$fname] ) {
$cache[$tname][$fname] = array();
$rows = $this->getRows("select id,$fname from $tname", 30*60);
foreach ( $rows as $row )
$cache[$tname][$fname][$row[$fname]] = $row['id'];
}
if ( @$cache[$tname][$fname][$name] )
return($cache[$tname][$fname][$name]);
if ( ! $make )
return(null);
$id = $this->_dbInsert($tname, array(
$fname => $name,
));
if ( ! $id )
return(null);
$cache[$tname][$fname][$name] = $id;
return($cache[$tname][$fname][$name]);
}
/*------------------------------------------------------------*/
public function lastError() {
return($this->lastError);
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
private function ammend(&$data, $cols, $insert = false) {
$loginName = Mlogin::get('MloginName');
if ( $insert && in_array('createdOn', $cols) )
$data['createdOn'] = date("Y-m-d G:i:s");
if ( $loginName && $insert && in_array('createdBy', $cols) )
$data['createdBy'] = $loginName;
if ( in_array('lastChange', $cols) )
$data['lastChange'] = date("Y-m-d G:i:s");
if ( $loginName && in_array('lastChangeBy', $cols) )
$data['lastChangeBy'] = $loginName;
}
/*------------------------------*/
private function dbInsertSql($tableName, $data, $withId = false) {
$cols = $this->columns($tableName);
if ( ! $cols )
return(null);
$row = array();
$idName = $this->autoIncrement($tableName);
foreach ( $data as $fname => $value ) {
if ( ! in_array($fname, $cols) )
continue;
if ( $fname == $idName )
continue;
$dataType = $this->dataType($tableName, $fname);
if ( $dataType == 'timestamp' )
continue;
if ( $dataType == 'date' && ($value = Mdate::scan($value)) == null )
continue;
if ( $dataType == 'datetime' && ($value = Mdate::datetimeScan($value)) == null )
continue;
$str = $value;
if ( $str === null )
continue;
$row[$fname] = $str;
}
$insertData = array();
foreach ( $row as $fname => $value )
$insertData[$fname] = $value;
$this->ammend($insertData, $cols, true);
$fieldList = '`'.implode('`,`', array_keys($insertData)).'`';
$valueList = array();
$values = array();
foreach ( $insertData as $value )
if ( $value === 'now()' )
$values[] = 'now()';
else
$values[] = "'".$this->str($value)."'";
$valuesList = implode(",", $values);
$sql = "insert into $tableName ( $fieldList ) values ( $valuesList )";
return($sql);
}
/*------------------------------------------------------------*/
/*
* do fields have equivalent values:
* nulls and empty strings, dates in int or dashed format are equivalent
* updating of equivalent values is skipped
*/
private function equiv($a, $b) {
if ( $a === $b )
return(true);
$alen = strlen($a);
$blen = strlen($b);
// dates
if (
( $alen == 10 || $alen == 8 )
&& str_replace('-', '', $a) === str_replace('-', '', $b)
)
return(true);
// text
if (
str_replace("\r\n", "\n", $a) === str_replace("\r\n", "\n", $b)
)
return(true);
return(false);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
/**
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* requires Smarty software
*/
if ( ! class_exists("Smarty") )
require_once("Smarty-2.6.9/libs/Smarty.class.php");
/*------------------------------------------------------------*/
require_once("msu.php");
require_once("Mdate.class.php");
/*------------------------------------------------------------*/
/**
* Mview - View class
*
* Mview is an extension of the class Smarty (smarty.php.net)
*
* @package M
* @author Ohad Aloni
*/
class Mview extends Smarty {
/*------------------------------------------------------------*/
// messages and error require no construction with new()
/*------------------------------------------------------------*/
/**
* @var bool
*/
private $templateDirPath = array();
/*------------------------------------------------------------*/
function __construct() {
if ( ! defined('SMARTY_TPL_DIR') )
define('SMARTY_TPL_DIR', 'tpl');
if ( ! defined('SMARTY_RUN_DIR') )
define('SMARTY_RUN_DIR', 'smarty');
$this->use_sub_dirs = false;
$smartyTplDir = SMARTY_TPL_DIR ;
$smartyRunDir = SMARTY_RUN_DIR ;
$this->template_dir = $smartyTplDir;
$this->prependTemplateDir($smartyTplDir);
if ( defined('M_DIR') )
$this->appendTemplateDir(M_DIR."/tpl");
$this->compile_dir = "$smartyRunDir/compile/";
$this->config_dir = "$smartyRunDir/config/";
$this->cache_dir = "$smartyRunDir/cache/";
$this->registerFunction('msuShowTpl');
$this->registerFunction('msuVarDump');
$this->registerFunction('showMsg');
$this->registerFunction('showError');
$this->registerFunction('msuSetTitle');
$this->registerFunction('msuSetStatus');
$this->registerModifier('msuImplode');
$this->registerModifier('msuMoneyFmt');
$this->registerModifier('msuFloatFmt');
$this->registerModifier('msuDateFmt');
$this->registerModifier('msuDateTimePickerFmt');
$this->registerModifier('msuIntFmt');
$this->registerModifier('msuTimeFmt');
$this->registerModifier('htmlspecialchars');
$this->registerModifier('undash', 'Mdate');
$this->assign(array(
"yesNo" => array(
0 => 'No',
1 => 'Yes',
),
));
}
/*------------------------------------------------------------*/
private function registerClass($method, $class) {
if ( function_exists($method) )
return(true);
if ( $class ) {
if ( ! class_exists($class) ) {
require_once("$class.class.php");
if ( ! class_exists($class) ) {
self::error("Mview::registerClass: class $class not found");
return(null);
}
}
if ( method_exists($class, $method) )
return($class);
else {
self::error("Mview::registerClass: method $method not found in $class");
return(null);
}
}
if ( method_exists("Mutils", $method) )
return("Mutils");
self::error("Mview::registerClass: method $method not found");
return(null);
}
/*------------------------------*/
/**
* register a smarty plugin function
*
* @param string can be any function or method in Mview or Mutils or the passed class
* @param string
*/
private function registerFunction($method, $class = null) {
$callable = array($this, $method);
if ( is_callable($callable) ) {
$this->register_function($method, $callable);
return;
}
$class = $this->registerClass($method, $class);
if ( ! $class )
return;
if ( $class === true )
$this->register_function($method, $method, false);
else
$this->register_function($method, array($class, $method,));
}
/*------------------------------------------------------------*/
/**
* register a smarty plugin filter (modifier)
*
* @param string can be any function or method in Mview or Mutils or the passed class
* @param string
*/
public function registerModifier($method, $class = null) {
$class = $this->registerClass($method, $class);
if ( ! $class )
return;
if ( $class === true )
$this->register_modifier($method, $method);
else
$this->register_modifier($method, array($class, $method,));
}
/*------------------------------------------------------------*/
/**
* prepend a template folder to the template search path
*
* for use with {msuShowTpl file=... arg1=...} -
*
* when using include in templates, only the first $smarty->template_dir is recognized
*
*/
public function prependTemplateDir($dir) {
if ( ! in_array($dir, $this->templateDirPath) )
array_unshift($this->templateDirPath, $dir);
}
/*------------------------------*/
/**
* append a template folder to the template search path
*
* for use with {msuShowTpl file=... arg1=...} -
*
* when using include in templates, only the first $smarty->template_dir is recognized
*
*/
public function appendTemplateDir($dir) {
if ( ! in_array($dir, $this->templateDirPath) )
$this->templateDirPath[] = $dir;
}
/*------------------------------------------------------------*/
public function templateDirPath() {
$templateDirPath = implode(":", $this->templateDirPath);
return($templateDirPath);
}
/*------------------------------------------------------------*/
private function _render($tpl, $errorLogger = null) {
if ( ! is_writable($this->compile_dir) ) {
$pwd = trim(`pwd`);
$error = "Smarty Compile Dir $pwd/{$this->compile_dir} not writable";
$this->error($error);
if ( $errorLogger )
$errorLogger->log($error);
return(false);
}
if ( is_readable($tpl) )
return($this->fetch($tpl));
$cwd = getcwd();
foreach ( $this->templateDirPath as $dir ) {
if ( is_readable("$cwd/$dir/$tpl") )
return($this->fetch("$cwd/$dir/$tpl"));
if ( is_readable("$dir/$tpl") )
return($this->fetch("$dir/$tpl"));
}
if ( isset($this->template_dir) ) {
$td = $this->template_dir;
if ( is_readable("$td/$tpl") )
return($this->fetch($tpl));
}
$templateDirPath = $this->templateDirPath();
$error = "Mview: $tpl not found in $templateDirPath";
$this->error($error);
if ( $errorLogger )
$errorLogger->log($error);
return(null);
}
/*------------------------------------------------------------*/
public function renderText($text, $args = array(), $errorLogger = null) {
$args['evalText'] = $text;
$rendered = $this->render("eval.tpl", $args, $errorLogger);
return($rendered);
}
/*------------------------------------------------------------*/
/**
* return a rendered template
*/
public function render($tpl, $args = null, $errorLogger = null) {
if ( is_array($args) ) {
$this->assign($args);
$this->assign(array('tplArgs' => $args));
}
$rendered = $this->_render($tpl, $errorLogger);
if ( is_array($args) ) {
$keys = array_keys($args);
/* $this->clear_assign($keys); */
/* $this->clear_assign('tplArgs'); */
}
return($rendered);
}
/*------------------------------------------------------------*/
private static $holdOutput = false;
private static $outputBuffer = "";
/*------------------------------*/
public static function holdOutput() {
self::$holdOutput = true;
}
/*------------------------------*/
public static function flushOutput() {
// msgbuf had better been output by now by app, not usable after this;
$Msession = new Msession;
$msgBuf = $Msession->get('msgBuf');
if ( $msgBuf )
$Msession->set('msgBuf', array()); // sets cookie - must be bfore next...
if ( self::$outputBuffer ) {
echo self::$outputBuffer;
self::$outputBuffer = "";
}
flush();
@ob_flush(); // may have been turned off by ob_implicit_flush()
}
/*------------------------------*/
public static function pushOutput($htmlText) {
if ( self::$holdOutput )
self::$outputBuffer .= $htmlText;
else
echo $htmlText;
}
/*------------------------------*/
/**
* show a template
*
* @param string file name
* @param array list of named arguments
* @param fetch only return the rendered template and do not display if true
*
*/
public function showTpl($tpl = null, $args = null, $fetch = false) {
if ( $tpl == null )
$tpl = @$_REQUEST['tpl'];
$fetched = $this->render($tpl, $args);
if ( ! $fetch )
self::pushOutput($fetched);
return($fetched);
}
/*------------------------------*/
public function permit() {
// allow automatic urls /Mview/showTpl?tpl=
return(true);
}
/*------------------------------------------------------------*/
public static function showRows($rows, $exportFileName = null) {
global $Mview; // need an instance anyway
if ( ! $rows || ! is_array($rows) || count($rows) == 0 ) {
self::msg("No Rows");
return;
}
$columns = array_keys($rows[0]);
if ( ! $Mview )
$Mview = new Mview;
$Mview->showTpl("mShowRows.tpl", array(
'columns' => $columns,
'rows' => $rows,
'exportFileName' => $exportFileName,
));
}
/*------------------------------------------------------------*/
private static $msgBuf = array();
private static $isHold = false;
/*------------------------------*/
/**
* buffered messages
*/
public function messages() {
return(self::$msgBuf);
}
/*------------------------------*/
/**
* buffer requested messages and errors
* do not show anything at least until a further notice by flushMsgs()
*/
public function holdMsgs() {
self::$isHold = true;
}
/*------------------------------*/
/**
* flush messages and errors previously held due to a call to holdMsgs() and stop buffering
*/
function flushMsgs() {
self::$isHold = false ;
foreach ( self::$msgBuf as $msg )
self::message($msg['msg'], $msg['iserror']);
self::$msgBuf = array();
}
/*------------------------------*/
private static function message($msg, $iserror, $url = null) {
$me = get_class()."::".__FUNCTION__."()";
$isHtml = isset($_SERVER['REMOTE_ADDR']);
if ( $isHtml ) {
$type = $iserror ? "alert-danger" : "alert-info" ;
$msg = htmlspecialchars($msg);
$tokens = explode("\n", $msg);
$before = "";
for($i=0;$i<count($tokens);$i++){
if(trim($tokens[$i])!= "")
break;
if(trim($tokens[$i])== "")
$before.="<br/>";
}
$tokens = array_reverse($tokens);
$after = "";
for($i=0;$i<count($tokens);$i++){
if(trim($tokens[$i])!= "")
break;
if(trim($tokens[$i]) == "")
$after.="<br/>";
}
$msg = trim($msg,"\n");
$msg = nl2br($msg);
if ($url){
$msg = "<a target=\"_blank\" href=\"$url\">$msg</a>";
}
$text = "<div class='alert $type'><strong>$msg</strong></div>" ;
$text = $before.$text.$after;
}
else {
$pfx = $iserror ? "ERROR: " : "" ;
$text = "$pfx$msg\n" ;
}
if ( $isHtml ) {
$Msession = new Msession;
$sessionMsgBuf = $Msession->get('msgBuf');
if ( ! $sessionMsgBuf )
$sessionMsgBuf = array();
$numMessages = count($sessionMsgBuf);
if ( $numMessages >= 7 ) {
$lastText = $sessionMsgBuf[$numMessages-1];
if ( $lastText != '...' ) {
$sessionMsgBuf[] = '...';
$Msession->set('msgBuf', $sessionMsgBuf);
}
} else {
$sessionMsgBuf[] = $text;
$Msession->set('msgBuf', $sessionMsgBuf);
}
}
self::pushOutput($text);
}
/*------------------------------*/
/**
* show a msg (or hold it - see holdMsgs())
*
* @param string
*/
public static function msg($msg, $iserror = false, $url = null) {
if ( self::$isHold )
self::$msgBuf[] = array('msg' => $msg, 'iserror' => $iserror, 'url' => $url, );
else
self::message($msg, $iserror, $url);
}
/*------------------------------*/
/**
* show an error (or hold it - see holdMsgs())
*
* @param string
*/
public static function error($msg) {
error_log($msg);
self::msg($msg, true);
}
/*------------------------------*/
public static function urlMsg($msg, $url) {
self::msg($msg, false, $url);
}
/*------------------------------------------------------------*/
/**
* show a message from a template
*
* {showMsg msg="..."}
*/
public function showMsg($a) { self::msg($a['msg']); }
/*------------------------------*/
/**
* show an error from a template
*
* {showError msg="..."}
*/
public function showError($a) { self::error($a['msg']); }
/*------------------------------------------------------------*/
/**
* a fancier version of print_r helps debugging by showing a title, file and line number
* in a more legible display
*
*
* e.g<br />
* Mview::print_r($_REQUEST, "_REQUEST", __FILE__, __LINE__);
*
* @param mixed
* @param string
* @param string
* @param int
*
*/
public static function print_r($var, $varName = null, $file = null, $line = null, $return = false, $logError = false) {
$print_r = print_r($var, true);
if ( $file ) {
$fileParts = explode("/", trim($file, "/"));
$fileName = $fileParts[count($fileParts)-1];
}
if ( $logError ) {
$str = str_replace("\n", " --> ", $print_r);
$str = "$fileName:$line: $varName: [[[ $print_r ]]]";
error_log($str);
return;
}
$isHtml = isset($_SERVER['REMOTE_ADDR']);
$ret = "";
if ( $isHtml )
$ret .= "\n<table border=\"0\"><tr><td align=\"left\"><pre>\n";
if ( $file ) {
$ret .= "$varName ($fileName: $line)\n--------------------------------------------------------------\n";
}
$ret .= $print_r;
$ret .= "\n";
if ( $isHtml )
$ret .= "\n</pre></td></tr></table>\n";
if ( ! $return )
self::pushOutput($ret);
return($ret);
}
/*------------------------------------------------------------*/
/**
*
* escape quotes for javascript
*
* @param string
* @return string
*/
public function jsStr($str) {
// escape with \ but
// if they are already escaped ...
$ret = str_replace("\\'", "'", $str);
$ret = str_replace("'", "\\'", $ret);
$ret = str_replace("\r\n", "\n", $ret);
$ret = str_replace("\\n", "\n", $ret);
$ret = str_replace("\n", "\\n", $ret);
return($ret);
}
/*------------------------------*/
/**
* execute javascript by echoing it wrapped in html and flushing output buffers for immeadiate execution
*
* @param string
*/
public function js($s) {
echo "<script type=\"text/javascript\"> $s </script>\n" ;
flush();
ob_flush();
}
/*------------------------------*/
/**
* set title of page using javascript
*
* @param string
*/
public function jsTitle($s) {
$title = $this->jsStr($s);
$this->js("document.title = '$title' ; ");
}
/*------------------------------------------------------------*/
public static function setCookie($name, $value, $expires = null) {
if ( $expires < 0 ) {
unset($_COOKIE[$name]);
@setcookie($name, null, time(0) - 3, "/");
return;
}
$defaultExpires = 10*365*24*60*60;
if ( $expires == null )
$expires = $defaultExpires;
if ( $expires <= $defaultExpires )
$expires += time();
if ( @setcookie($name, $value, $expires, "/") ) {
$_COOKIE[$name] = $value;
} else {
/* self::error("Cannot set cookie - output already started"); */
/* Mutils::trace(); */
/* self::flush(); */
/* exit; */
}
}
/*------------------------------------------------------------*/
/**
* include a template
* {msuShowTpl file="abc.tpl" a=... b=... c=...}
*/
public function msuShowTpl($a) {
$tpl = $a['file'];
$b = $a ;
$b['tplArgs'] = $a;
$rendered = $this->showTpl($tpl, $b, true);
// call from a smarty template -
// skip output buffering or output will be reversed.
echo $rendered;
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
<?php
/*------------------------------------------------------------*/
/**
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* Mmodel and Mview are accessible from Mcontroller and any class
* that extends Mcontroller
*/
require_once("Mmodel.class.php");
require_once("Mview.class.php");
/*------------------------------------------------------------*/
/**
*
* Mcontroller has several control functions:
* 1. Allow control to flow only if appropriate permission conditions are met.<br />
* 2. Branch URLs of the form className=...&action=... to the corresponding class method.<br />
* 3. Serve as a superclass to be extended with seamless inheritance from Mmoel and Mview.<br />
*
* @package M
*/
/*------------------------------------------------------------*/
class Mcontroller {
/**
* @var controller name of the current controller
*/
protected $controller;
/**
* @var action name of the current action
*/
protected $action;
private $isConstructed = false;
private $controllerError; // to be requested in case false is returned.
/**
* @var Mmodel access the Mmodel class from this instance
*/
public $Mmodel;
/**
* @var Mview access the Mview class from this instance
*/
public $Mview;
/*------------------------------------------------------------*/
/**
* Mcontroller is typically extended with no arguments
* or created with no arguments.
*
* @param Mmodel force use of this instance
* @param Mview force use of this instance
*
*/
function __construct($Mm = null, $Mv = null) {
global $Mmodel;
global $Mview;
if ( $Mm !== null )
$this->Mmodel = $Mm;
elseif ( isset($Mmodel) && $Mmodel != null )
$this->Mmodel = $Mmodel;
else
$this->Mmodel = new Mmodel();
if ( $Mv !== null )
$this->Mview = $Mv;
elseif ( isset($Mview) && $Mview != null )
$this->Mview = $Mview;
else
$this->Mview = new Mview();
if ( ! $this->Mmodel ) {
$stack = debug_backtrace(false);
Mview::print_r($stack, "stack", __FILE__, __LINE__);
$this->quit();
exit;
}
if ( $this->Mmodel->isConnected() )
$this->isConstructed = true;
}
/*------------------------------*/
public function isConstructed() {
return($this->isConstructed);
}
/*------------------------------------------------------------*/
public function _control($silent = false) {
return($this->control(null, null, null, $silent));
}
/*------------------------------------------------------------*/
public function lastControllerError() {
return($this->controllerError);
}
/*------------------------------------------------------------*/
private function controllerError($msg, $silent = false) {
$this->controllerError = $msg;
if ( $silent )
error_log($msg);
else
$this->Mview->error($msg);
}
/*------------------------------------------------------------*/
public function control($className = null, $action = null, $args = null, $silent = false) {
$requestArgs = array();
if ( is_string($args) ) {
$vars = explode('&', $args);
foreach ( $vars as $var ) {
$nv = explode('=', $var);
if ( count($nv) != 2 ) {
$this->controllerError("$var ???", $silent);
return(false);
}
list($n, $v) = $nv;
$requestArgs[$n] = $v;
}
} else if ( $args ) {
foreach ( $args as $key => $arg )
$requestArgs[$key] = $arg;
}
$obj = $this->obj($className, $silent);
if ( ! $obj ) {
return(false);
}
$action = $this->action($action);
if ( $action == null )
$action = "index";
if ( ! is_callable(array($obj, $action)) ) {
$className = get_class($obj);
$this->controllerError("Method '$action' not callable in class '$className'");
return(false);
}
$className = get_class($obj);
$this->controller = strtolower($className);
$this->action = strtolower($action);
$obj->controller = strtolower($className);
$obj->action = strtolower($action);
Mutils::setenv("controller", $this->controller);
Mutils::setenv("action", $this->action);
$savedRequestArgs = $this->setRequestArgs($requestArgs);
if ( ! $obj->permit($className, $action) )
return(false);
if ( method_exists($obj, "before") ) // e.g. Mmodel auto-autocomplete does not extend Mcontroller
$obj->before();
$obj->$action();
if ( method_exists($obj, "after") ) // e.g. Mmodel auto-autocomplete does not extend Mcontroller
$obj->after();
$this->revertRequestArgs($requestArgs, $savedRequestArgs);
return(true);
}
/*------------------------------*/
private function setRequestArgs($requestArgs) {
$savedRequestArgs = array();
foreach ( $requestArgs as $key => $arg ) {
if ( array_key_exists($key, $_REQUEST) )
$savedRequestArgs[$key] = $_REQUEST[$key];
$_REQUEST[$key] = $arg;
}
return($savedRequestArgs);
}
/*------------------------------*/
private function revertRequestArgs($requestArgs, $savedRequestArgs) {
foreach ( $requestArgs as $key => $arg )
unset($_REQUEST[$key]);
foreach ( $savedRequestArgs as $key => $arg )
$_REQUEST[$key] = $arg;
}
/*------------------------------*/
protected function before() {}
protected function after() {}
/*------------------------------------------------------------*/
private function obj($className, $silent = false) {
if ( ($className = $this->className($className)) == null )
return($this);
if ( class_exists($className) ) {
$obj = new $className;
return($obj);
}
$files = Mutils::listDir(".", "php");
// git problems - Fri Dec 14 15:06:35 IST 2012
foreach ( $files as $file ) {
$fileParts = explode(".", $file);
$baseName = reset($fileParts);
if(strtolower($className) != strtolower($baseName) )
continue;
require_once($file);
if ( class_exists($baseName) ) {
$obj = new $baseName;
return($obj);
}
$this->controllerError("class $baseName not found in $file", $silent);
return(false);
}
$this->controllerError("cannot find class for '$className'", $silent);
return(null);
}
/*------------------------------------------------------------*/
public function redirect($url = null) {
if ( $url && substr($url, 0, 4) == "http" ) {
header("Location: $url");
exit;
}
if ( ! $url ) {
$url = $this->controller;
if ( $this->action )
$url .= "/".$this->action;
}
if ( $url == "/" )
$url = "";
$serverName = $_SERVER['SERVER_NAME'];
$isHttps = @$_SERVER['HTTPS'] == "on";
$s = $isHttps ? "s" : "";
$url = trim($url, "/");
header("Location: http$s://$serverName/$url");
exit;
}
/*------------------------------------------------------------*/
/**
* decide whether to allow the execution of a method by the user
*
* permit() is by default suitable for public access.
* It returns true thus allowing everything.<br />
* In secure and controlled systems, permit() is overridden by the extending class
* to check login credentials
*
* @return bool
*/
protected function permit() {
return(true);
}
/*------------------------------------------------------------*/
/**
* when a URL specifies a controller without an action
* the method index() is called.
*
*/
public function index() {
$className = get_class($this);
$this->Mview->error("$className: method index() not defined");
return(null);
}
/*------------------------------*/
public function defaultAction() { $this->index(); }
public function main() { $this->index(); }
/*------------------------------------------------------------*/
/**
* show an array on screen - for developing and debugging
*
* @param array
*/
public function showArray($a) {
$this->Mview->showTpl("mShowArray.tpl", array(
'a' => $a,
));
}
/*------------------------------------------------------------*/
public function pathParts() {
if ( isset($_REQUEST['PATH_INFO']) )
$path = $_REQUEST['PATH_INFO'];
elseif ( isset($_SERVER['PATH_INFO']) )
$path = $_SERVER['PATH_INFO'];
else
return(null);
// ignore leading, trailing and duplicate slashes
$pathParts = array();
$parts = explode("/", $path);
foreach ( $parts as $part )
if ( $part != "" )
$pathParts[] = $part;
return($pathParts);
}
/*------------------------------------------------------------*/
protected static $debugLevel = 0;
/*------------------------------*/
public static function debugLevel($level = null) {
$currentLevel = self::$debugLevel;
$requestLevel = @$_REQUEST['debugLevel'];
$envLevel = Mutils::getenv("debugLevel");
$newLevel = 0;
if ( $level != null )
$newLevel = $level;
if ( $currentLevel > $newLevel )
$newLevel = $currentLevel;
if ( $requestLevel > $newLevel )
$newLevel = $requestLevel;
if ( $envLevel > $newLevel )
$newLevel = $envLevel;
if ( self::$debugLevel != $newLevel )
Mutils::setenv("debugLevel", $newLevel);
self::$debugLevel = $newLevel;
return($newLevel);
}
/*------------------------------*/
public function debug($file, $lineNo, $tag, $msg = null, $debugLevelAtLeast = 1) {
$debugLevel = $this->debugLevel();
if ( self::$debugLevel < $debugLevelAtLeast )
return;
$datetime = date("Y-m-d G:i:s");
$fileName = basename($file);
$text = "$datetime: $fileName:$lineNo:$tag";
if ( $msg )
$text .= ": $msg";
$isHttp = @$_SERVER['SERVER_ADDR'] != null;
if ( $isHttp )
$text = htmlspecialchars($text)."<br />";
echo "$text\n";
}
/*------------------------------------------------------------*/
public function space($tag) {
$me = get_class()."::".__FUNCTION__."()";
$space = Perf::space();
Mview::msg("$tag: $space space");
return($space);
}
/*------------------------------------------------------------*/
public function exportToExcel($rows, $fileName = null) {
if ( count($rows) == 0 ) {
$this->Mview->msg("No Rows");
return;
}
$headings = array_keys($rows[0]);
$content = $this->Mview->render("excel.tpl", array(
'headings' => $headings,
'rows' => $rows,
));
if ( ! $fileName && isset($_REQUEST['fileName']) )
$fileName = $_REQUEST['fileName'];
if ( ! $fileName )
$fileName = "M2excel-".date("Ymd");
$filesize = strlen($content);
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=$fileName.xls");
header("Content-Length: $filesize");
echo $content;
}
/*------------------------------------------------------------*/
/**
*
* show rows on screen
*
* @param string the query
* @param boolean show number of rows on screen
* @param string the filename that the browser will offer to 'save as'
*/
public function showRowsFromSql($sql, $showCount = true, $exportFileName = null) {
if ( ! $exportFileName )
$exportFileName = "M2excel-".date("Ymd");
$res = $this->Mmodel->query($sql);
if ( $res === null )
return;
$numRows = 0 ;
$isheaded = false;
$numRowsSpanId = "numRows".rand(1, 1000000);
$numRowsSSpanId = "numRowsS".rand(1, 1000000);
while ( $row = @mysqli_fetch_assoc($res) ) {
$numRows++;
if ( ! $isheaded ) {
$headings = array_keys($row);
$this->Mview->showTpl("mShowRowsHeader.tpl", array(
"sql" => $sql,
'showCount' => $showCount,
'columns' => $headings,
'exportFileName' => $exportFileName,
'numRowsSpanId' => $numRowsSpanId,
'numRowsSSpanId' => $numRowsSSpanId,
));
$isheaded = true;
}
$this->Mview->showTpl("mShowRowsRow.tpl", array("row" => $row,));
}
if ( $isheaded )
$this->Mview->showTpl("mShowRowsFooter.tpl", array(
'numRows' => $numRows,
'numRowsSpanId' => $numRowsSpanId,
'numRowsSSpanId' => $numRowsSSpanId,
));
else
$this->Mview->error("No Rows");
}
/*------------------------------------------------------------*/
/**
/**
* show rows on screen
*
* @param array the rows to be shown
*
* each an associative array with indexes to be used for heading titles
* if rows is a string, it is an sql to fetch the rows and a streaming interface is used
* optional arguments tell wether to show the number of rows
* and set the dfefault file name for exporting
*/
public function showRows($rows, $showCount = false, $exportFileName = null) {
if ( is_string($rows) ) {
$this->showRowsFromSql($rows, $showCount, $exportFileName);
return;
}
if ( ! $rows || ! is_array($rows) || count($rows) == 0 ) {
$this->Mview->msg("No Rows");
return;
}
$headings = array_keys($rows[0]);
$this->Mview->showTpl("mShowRows.tpl", array(
'showCount' => $showCount,
'columns' => $headings,
'rows' => $rows,
'exportFileName' => $exportFileName,
));
}
/*------------------------------------------------------------*/
public function quit() {
$this->Mview->flushOutput();
exit;
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
private function className($className = null) {
if ( $className )
return($className);
if ( isset($_POST['className']) && $_POST['className'] != '' )
return($_POST['className']);
if ( isset($_GET['className']) && $_GET['className'] != '' )
return($_GET['className']);
$pathParts = $this->pathParts();
$pr = print_r($pathParts, true);
return(isset($pathParts[0]) ? $pathParts[0] : null);
}
/*------------------------------------------------------------*/
private function action($action = null) {
if ( $action )
return($action);
if ( isset($_POST['action']) && $_POST['action'] != '' )
return($_POST['action']);
if ( isset($_GET['action']) && $_GET['action'] != '' )
return($_GET['action']);
$pathParts = $this->pathParts();
if ( isset($pathParts[1]) )
return($pathParts[1]);
return(null);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/