Added Kohana v3.0.8
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Kohana user guide and api browser.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Controllers
|
||||
* @author Kohana Team
|
||||
*/
|
||||
class Controller_Userguide extends Controller_Template {
|
||||
|
||||
public $template = 'userguide/template';
|
||||
|
||||
// Routes
|
||||
protected $media;
|
||||
protected $api;
|
||||
protected $guide;
|
||||
|
||||
public function before()
|
||||
{
|
||||
if ($this->request->action === 'media')
|
||||
{
|
||||
// Do not template media files
|
||||
$this->auto_render = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Grab the necessary routes
|
||||
$this->media = Route::get('docs/media');
|
||||
$this->guide = Route::get('docs/guide');
|
||||
|
||||
if (isset($_GET['lang']))
|
||||
{
|
||||
$lang = $_GET['lang'];
|
||||
|
||||
// Load the accepted language list
|
||||
$translations = array_keys(Kohana::message('userguide', 'translations'));
|
||||
|
||||
if (in_array($lang, $translations))
|
||||
{
|
||||
// Set the language cookie
|
||||
Cookie::set('userguide_language', $lang, Date::YEAR);
|
||||
}
|
||||
|
||||
// Reload the page
|
||||
$this->request->redirect($this->request->uri);
|
||||
}
|
||||
|
||||
// Set the translation language
|
||||
I18n::$lang = Cookie::get('userguide_language', Kohana::config('userguide')->lang);
|
||||
|
||||
if (defined('MARKDOWN_PARSER_CLASS'))
|
||||
{
|
||||
throw new Kohana_Exception('Markdown parser already registered. Live documentation will not work in your environment.');
|
||||
}
|
||||
|
||||
// Use customized Markdown parser
|
||||
define('MARKDOWN_PARSER_CLASS', 'Kodoc_Markdown');
|
||||
|
||||
if ( ! class_exists('Markdown', FALSE))
|
||||
{
|
||||
// Load Markdown support
|
||||
require Kohana::find_file('vendor', 'markdown/markdown');
|
||||
}
|
||||
|
||||
// Set the base URL for links and images
|
||||
Kodoc_Markdown::$base_url = URL::site($this->guide->uri()).'/';
|
||||
Kodoc_Markdown::$image_url = URL::site($this->media->uri()).'/';
|
||||
}
|
||||
|
||||
parent::before();
|
||||
}
|
||||
|
||||
public function action_docs()
|
||||
{
|
||||
$page = $this->request->param('page');
|
||||
|
||||
if ( ! $page)
|
||||
{
|
||||
// Redirect to the default page
|
||||
$this->request->redirect($this->guide->uri(array('page' => Kohana::config('userguide')->default_page)));
|
||||
}
|
||||
|
||||
$file = $this->file($page);
|
||||
|
||||
if ( ! $file)
|
||||
{
|
||||
$this->error(__('Userguide page not found'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the page title
|
||||
$this->template->title = $this->title($page);
|
||||
|
||||
// Parse the page contents into the template
|
||||
$this->template->content = Markdown(file_get_contents($file));
|
||||
|
||||
// Attach the menu to the template
|
||||
$this->template->menu = Markdown(file_get_contents($this->file('menu')));
|
||||
|
||||
// Bind module menu items
|
||||
$this->template->bind('module_menus', $module_menus);
|
||||
|
||||
// Attach module-specific menu items
|
||||
$module_menus = array();
|
||||
|
||||
foreach(Kohana::modules() as $module => $path)
|
||||
{
|
||||
if ($file = $this->file('menu.'.$module))
|
||||
{
|
||||
$module_menus[$module] = Markdown(file_get_contents($file));
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the breadcrumb
|
||||
$this->template->bind('breadcrumb', $breadcrumb);
|
||||
|
||||
// Add the breadcrumb
|
||||
$breadcrumb = array();
|
||||
$breadcrumb[$this->guide->uri()] = __('User Guide');
|
||||
$breadcrumb[] = $this->section($page);
|
||||
$breadcrumb[] = $this->template->title;
|
||||
}
|
||||
|
||||
public function action_api()
|
||||
{
|
||||
// Enable the missing class autoloader
|
||||
spl_autoload_register(array('Kodoc_Missing', 'create_class'));
|
||||
|
||||
// Get the class from the request
|
||||
$class = $this->request->param('class');
|
||||
|
||||
if ($class)
|
||||
{
|
||||
try
|
||||
{
|
||||
$_class = Kodoc_Class::factory($class);
|
||||
|
||||
if ( ! Kodoc::show_class($_class))
|
||||
throw new Exception(__('That class is hidden'));
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return $this->error(__('API Reference: Class not found.'));
|
||||
}
|
||||
|
||||
$this->template->title = $class;
|
||||
|
||||
$this->template->content = View::factory('userguide/api/class')
|
||||
->set('doc', Kodoc::factory($class))
|
||||
->set('route', $this->request->route);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->template->title = __('Table of Contents');
|
||||
|
||||
$this->template->content = View::factory('userguide/api/toc')
|
||||
->set('classes', Kodoc::class_methods())
|
||||
->set('route', $this->request->route);
|
||||
}
|
||||
|
||||
// Attach the menu to the template
|
||||
$this->template->menu = Kodoc::menu();
|
||||
|
||||
// Bind the breadcrumb
|
||||
$this->template->bind('breadcrumb', $breadcrumb);
|
||||
|
||||
// Get the docs URI
|
||||
$guide = Route::get('docs/guide');
|
||||
|
||||
// Add the breadcrumb
|
||||
$breadcrumb = array();
|
||||
$breadcrumb[$this->guide->uri(array('page' => NULL))] = __('User Guide');
|
||||
$breadcrumb[$this->request->route->uri()] = $this->title('api');
|
||||
$breadcrumb[] = $this->template->title;
|
||||
}
|
||||
|
||||
public function action_media()
|
||||
{
|
||||
// Generate and check the ETag for this file
|
||||
$this->request->check_cache(sha1($this->request->uri));
|
||||
|
||||
// Get the file path from the request
|
||||
$file = $this->request->param('file');
|
||||
|
||||
// Find the file extension
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
// Remove the extension from the filename
|
||||
$file = substr($file, 0, -(strlen($ext) + 1));
|
||||
|
||||
if ($file = Kohana::find_file('media', $file, $ext))
|
||||
{
|
||||
// Send the file content as the response
|
||||
$this->request->response = file_get_contents($file);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return a 404 status
|
||||
$this->request->status = 404;
|
||||
}
|
||||
|
||||
// Set the proper headers to allow caching
|
||||
$this->request->headers['Content-Type'] = File::mime_by_ext($ext);
|
||||
$this->request->headers['Content-Length'] = filesize($file);
|
||||
$this->request->headers['Last-Modified'] = date('r', filemtime($file));
|
||||
}
|
||||
|
||||
// Display an error if a page isn't found
|
||||
public function error($message)
|
||||
{
|
||||
$this->request->status = 404;
|
||||
$this->template->title = __('User Guide').' - '.__('Error');
|
||||
$this->template->content = View::factory('userguide/error',array('message'=>$message));
|
||||
$this->template->menu = Kodoc::menu();
|
||||
$this->template->breadcrumb = array($this->guide->uri() => __('User Guide'), __('Error'));
|
||||
}
|
||||
|
||||
public function after()
|
||||
{
|
||||
if ($this->auto_render)
|
||||
{
|
||||
// Get the media route
|
||||
$media = Route::get('docs/media');
|
||||
|
||||
// Add styles
|
||||
$this->template->styles = array(
|
||||
$media->uri(array('file' => 'css/print.css')) => 'print',
|
||||
$media->uri(array('file' => 'css/screen.css')) => 'screen',
|
||||
$media->uri(array('file' => 'css/kodoc.css')) => 'screen',
|
||||
$media->uri(array('file' => 'css/shCore.css')) => 'screen',
|
||||
$media->uri(array('file' => 'css/shThemeKodoc.css')) => 'screen',
|
||||
);
|
||||
|
||||
// Add scripts
|
||||
$this->template->scripts = array(
|
||||
$media->uri(array('file' => 'js/jquery.min.js')),
|
||||
$media->uri(array('file' => 'js/kodoc.js')),
|
||||
$media->uri(array('file' => 'js/shCore.js')),
|
||||
$media->uri(array('file' => 'js/shBrushPhp.js')),
|
||||
);
|
||||
|
||||
// Add languages
|
||||
$this->template->translations = Kohana::message('userguide', 'translations');
|
||||
}
|
||||
|
||||
return parent::after();
|
||||
}
|
||||
|
||||
public function file($page)
|
||||
{
|
||||
if ( ! ($file = Kohana::find_file('guide', I18n::$lang.'/'.$page, 'md')))
|
||||
{
|
||||
// Use the default file
|
||||
$file = Kohana::find_file('guide', $page, 'md');
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
public function section($page)
|
||||
{
|
||||
$markdown = $this->_get_all_menu_markdown();
|
||||
|
||||
if (preg_match('~\*{2}(.+?)\*{2}[^*]+\[[^\]]+\]\('.preg_quote($page).'\)~mu', $markdown, $matches))
|
||||
{
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public function title($page)
|
||||
{
|
||||
$markdown = $this->_get_all_menu_markdown();
|
||||
|
||||
if (preg_match('~\[([^\]]+)\]\('.preg_quote($page).'\)~mu', $markdown, $matches))
|
||||
{
|
||||
// Found a title for this link
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
protected function _get_all_menu_markdown()
|
||||
{
|
||||
// Only do this once per request...
|
||||
static $markdown = '';
|
||||
|
||||
if (empty($markdown))
|
||||
{
|
||||
// Get core menu items
|
||||
$file = $this->file('menu');
|
||||
|
||||
if ($file AND $text = file_get_contents($file))
|
||||
{
|
||||
$markdown .= $text;
|
||||
}
|
||||
|
||||
// Look in module specific files
|
||||
foreach(Kohana::modules() as $module => $path)
|
||||
{
|
||||
if ($file = $this->file('menu.'.$module) AND $text = file_get_contents($file))
|
||||
{
|
||||
// Concatenate markdown to produce one string containing all menu items
|
||||
$markdown .="\n".$text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
|
||||
} // End Userguide
|
3
includes/kohana/modules/userguide/classes/kodoc.php
Normal file
3
includes/kohana/modules/userguide/classes/kodoc.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc extends Kohana_Kodoc {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc_Class extends Kohana_Kodoc_Class {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc_Markdown extends Kohana_Kodoc_Markdown {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc_Method extends Kohana_Kodoc_Method {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc_Method_Param extends Kohana_Kodoc_Method_Param {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
abstract class Kodoc_Missing extends Kohana_Kodoc_Missing { }
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Kodoc_Property extends Kohana_Kodoc_Property {}
|
325
includes/kohana/modules/userguide/classes/kohana/kodoc.php
Normal file
325
includes/kohana/modules/userguide/classes/kohana/kodoc.php
Normal file
@@ -0,0 +1,325 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Documentation generator.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc {
|
||||
|
||||
public static function factory($class)
|
||||
{
|
||||
return new Kodoc_Class($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an html list of all classes sorted by category (or package if no category)
|
||||
*
|
||||
* @return string the html for the menu
|
||||
*/
|
||||
public static function menu()
|
||||
{
|
||||
$classes = Kodoc::classes();
|
||||
|
||||
foreach ($classes as $class)
|
||||
{
|
||||
if (isset($classes['kohana_'.$class]))
|
||||
{
|
||||
// Remove extended classes
|
||||
unset($classes['kohana_'.$class]);
|
||||
}
|
||||
}
|
||||
|
||||
ksort($classes);
|
||||
|
||||
$menu = array();
|
||||
|
||||
$route = Route::get('docs/api');
|
||||
|
||||
foreach ($classes as $class)
|
||||
{
|
||||
$class = Kodoc_Class::factory($class);
|
||||
|
||||
// Test if we should show this class
|
||||
if ( ! Kodoc::show_class($class))
|
||||
continue;
|
||||
|
||||
$link = HTML::anchor($route->uri(array('class' => $class->class->name)), $class->class->name);
|
||||
|
||||
if (isset($class->tags['package']))
|
||||
{
|
||||
foreach ($class->tags['package'] as $package)
|
||||
{
|
||||
if (isset($class->tags['category']))
|
||||
{
|
||||
foreach ($class->tags['category'] as $category)
|
||||
{
|
||||
$menu[$package][$category][] = $link;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$menu[$package]['Base'][] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$menu['[Unknown]']['Base'][] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the packages
|
||||
ksort($menu);
|
||||
|
||||
return View::factory('userguide/api/menu')
|
||||
->bind('menu', $menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the classes available, built by listing all files in the classes folder and then trying to create that class.
|
||||
*
|
||||
* This means any empty class files (as in complety empty) will cause an exception
|
||||
*
|
||||
* @param array array of files, obtained using Kohana::list_files
|
||||
* @return array an array of all the class names
|
||||
*/
|
||||
public static function classes(array $list = NULL)
|
||||
{
|
||||
if ($list === NULL)
|
||||
{
|
||||
$list = Kohana::list_files('classes');
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
|
||||
foreach ($list as $name => $path)
|
||||
{
|
||||
if (is_array($path))
|
||||
{
|
||||
$classes += Kodoc::classes($path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove "classes/" and the extension
|
||||
$class = substr($name, 8, -(strlen(EXT)));
|
||||
|
||||
// Convert slashes to underscores
|
||||
$class = str_replace(DIRECTORY_SEPARATOR, '_', strtolower($class));
|
||||
|
||||
$classes[$class] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all classes and methods of files in a list.
|
||||
*
|
||||
* > I personally don't like this as it was used on the index page. Way too much stuff on one page. It has potential for a package index page though.
|
||||
* > For example: class_methods( Kohana::list_files('classes/sprig') ) could make a nice index page for the sprig package in the api browser
|
||||
* > ~bluehawk
|
||||
*
|
||||
*/
|
||||
public static function class_methods(array $list = NULL)
|
||||
{
|
||||
$list = Kodoc::classes($list);
|
||||
|
||||
$classes = array();
|
||||
|
||||
foreach ($list as $class)
|
||||
{
|
||||
$_class = new ReflectionClass($class);
|
||||
|
||||
if (stripos($_class->name, 'Kohana') === 0)
|
||||
{
|
||||
// Skip the extension stuff stuff
|
||||
continue;
|
||||
}
|
||||
|
||||
$methods = array();
|
||||
|
||||
foreach ($_class->getMethods() as $_method)
|
||||
{
|
||||
$declares = $_method->getDeclaringClass()->name;
|
||||
|
||||
if (stripos($declares, 'Kohana') === 0)
|
||||
{
|
||||
// Remove "Kohana_"
|
||||
$declares = substr($declares, 7);
|
||||
}
|
||||
|
||||
if ($declares === $_class->name)
|
||||
{
|
||||
$methods[] = $_method->name;
|
||||
}
|
||||
}
|
||||
|
||||
sort($methods);
|
||||
|
||||
$classes[$_class->name] = $methods;
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a comment to extract the description and the tags
|
||||
*
|
||||
* @param string the comment retreived using ReflectionClass->getDocComment()
|
||||
* @return array array(string $description, array $tags)
|
||||
*/
|
||||
public static function parse($comment)
|
||||
{
|
||||
// Normalize all new lines to \n
|
||||
$comment = str_replace(array("\r\n", "\n"), "\n", $comment);
|
||||
|
||||
// Remove the phpdoc open/close tags and split
|
||||
$comment = array_slice(explode("\n", $comment), 1, -1);
|
||||
|
||||
// Tag content
|
||||
$tags = array();
|
||||
|
||||
foreach ($comment as $i => $line)
|
||||
{
|
||||
// Remove all leading whitespace
|
||||
$line = preg_replace('/^\s*\* ?/m', '', $line);
|
||||
|
||||
// Search this line for a tag
|
||||
if (preg_match('/^@(\S+)(?:\s*(.+))?$/', $line, $matches))
|
||||
{
|
||||
// This is a tag line
|
||||
unset($comment[$i]);
|
||||
|
||||
$name = $matches[1];
|
||||
$text = isset($matches[2]) ? $matches[2] : '';
|
||||
|
||||
switch ($name)
|
||||
{
|
||||
case 'license':
|
||||
if (strpos($text, '://') !== FALSE)
|
||||
{
|
||||
// Convert the lincense into a link
|
||||
$text = HTML::anchor($text);
|
||||
}
|
||||
break;
|
||||
case 'link':
|
||||
$text = preg_split('/\s+/', $text, 2);
|
||||
$text = HTML::anchor($text[0], isset($text[1]) ? $text[1] : $text[0]);
|
||||
break;
|
||||
case 'copyright':
|
||||
if (strpos($text, '(c)') !== FALSE)
|
||||
{
|
||||
// Convert the copyright sign
|
||||
$text = str_replace('(c)', '©', $text);
|
||||
}
|
||||
break;
|
||||
case 'throws':
|
||||
if (preg_match('/^(\w+)\W(.*)$/',$text,$matches))
|
||||
{
|
||||
$text = HTML::anchor(Route::get('docs/api')->uri(array('class' => $matches[1])), $matches[1]).' '.$matches[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
$text = HTML::anchor(Route::get('docs/api')->uri(array('class' => $text)), $text);
|
||||
}
|
||||
break;
|
||||
case 'uses':
|
||||
if (preg_match('/^([a-z_]+)::([a-z_]+)$/i', $text, $matches))
|
||||
{
|
||||
// Make a class#method API link
|
||||
$text = HTML::anchor(Route::get('docs/api')->uri(array('class' => $matches[1])).'#'.$matches[2], $text);
|
||||
}
|
||||
break;
|
||||
// Don't show @access lines, they are shown elsewhere
|
||||
case 'access':
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Add the tag
|
||||
$tags[$name][] = $text;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overwrite the comment line
|
||||
$comment[$i] = (string) $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Concat the comment lines back to a block of text
|
||||
if ($comment = trim(implode("\n", $comment)))
|
||||
{
|
||||
// Parse the comment with Markdown
|
||||
$comment = Markdown($comment);
|
||||
}
|
||||
|
||||
return array($comment, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source of a function
|
||||
*
|
||||
* @param string the filename
|
||||
* @param int start line?
|
||||
* @param int end line?
|
||||
*/
|
||||
public static function source($file, $start, $end)
|
||||
{
|
||||
if ( ! $file)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$file = file($file, FILE_IGNORE_NEW_LINES);
|
||||
|
||||
$file = array_slice($file, $start - 1, $end - $start + 1);
|
||||
|
||||
if (preg_match('/^(\s+)/', $file[0], $matches))
|
||||
{
|
||||
$padding = strlen($matches[1]);
|
||||
|
||||
foreach ($file as & $line)
|
||||
{
|
||||
$line = substr($line, $padding);
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a class should be shown, based on the api_packages config option
|
||||
*
|
||||
* @param Kodoc_Class the class to test
|
||||
* @return bool whether this class should be shown
|
||||
*/
|
||||
public static function show_class(Kodoc_Class $class)
|
||||
{
|
||||
$api_packages = Kohana::config('userguide.api_packages');
|
||||
|
||||
// If api_packages is true, all packages should be shown
|
||||
if ($api_packages === TRUE)
|
||||
return TRUE;
|
||||
|
||||
// Get the package tags for this class (as an array)
|
||||
$packages = Arr::get($class->tags,'package',Array('None'));
|
||||
|
||||
$show_this = FALSE;
|
||||
|
||||
// Loop through each package tag
|
||||
foreach ($packages as $package)
|
||||
{
|
||||
// If this package is in the allowed packages, set show this to true
|
||||
if (in_array($package,explode(',',$api_packages)))
|
||||
$show_this = TRUE;
|
||||
}
|
||||
|
||||
return $show_this;
|
||||
}
|
||||
|
||||
|
||||
} // End Kodoc
|
167
includes/kohana/modules/userguide/classes/kohana/kodoc/class.php
Normal file
167
includes/kohana/modules/userguide/classes/kohana/kodoc/class.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Class documentation generator.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc_Class extends Kodoc {
|
||||
|
||||
/**
|
||||
* @var ReflectionClass The ReflectionClass for this class
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/**
|
||||
* @var string modifiers like abstract, final
|
||||
*/
|
||||
public $modifiers;
|
||||
|
||||
/**
|
||||
* @var string description of the class from the comment
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @var array array of tags, retrieved from the comment
|
||||
*/
|
||||
public $tags = array();
|
||||
|
||||
/**
|
||||
* @var array array of this classes constants
|
||||
*/
|
||||
public $constants = array();
|
||||
|
||||
/**
|
||||
* Loads a class and uses [reflection](http://php.net/reflection) to parse
|
||||
* the class. Reads the class modifiers, constants and comment. Parses the
|
||||
* comment to find the description and tags.
|
||||
*
|
||||
* @param string class name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($class)
|
||||
{
|
||||
$this->class = new ReflectionClass($class);
|
||||
|
||||
if ($modifiers = $this->class->getModifiers())
|
||||
{
|
||||
$this->modifiers = '<small>'.implode(' ', Reflection::getModifierNames($modifiers)).'</small> ';
|
||||
}
|
||||
|
||||
if ($constants = $this->class->getConstants())
|
||||
{
|
||||
foreach ($constants as $name => $value)
|
||||
{
|
||||
$this->constants[$name] = Kohana::debug($value);
|
||||
}
|
||||
}
|
||||
|
||||
$parent = $this->class;
|
||||
|
||||
do
|
||||
{
|
||||
if ($comment = $parent->getDocComment())
|
||||
{
|
||||
// Found a description for this class
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ($parent = $parent->getParentClass());
|
||||
|
||||
list($this->description, $this->tags) = Kodoc::parse($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the class properties as [Kodoc_Property] objects.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function properties()
|
||||
{
|
||||
$props = $this->class->getProperties();
|
||||
|
||||
sort($props);
|
||||
|
||||
foreach ($props as $key => $property)
|
||||
{
|
||||
// Only show public properties, because Reflection can't get the private ones
|
||||
if ($property->isPublic())
|
||||
{
|
||||
$props[$key] = new Kodoc_Property($this->class->name, $property->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($props[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the class properties as [Kodoc_Method] objects.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function methods()
|
||||
{
|
||||
$methods = $this->class->getMethods();
|
||||
|
||||
usort($methods, array($this,'_method_sort'));
|
||||
|
||||
foreach ($methods as $key => $method)
|
||||
{
|
||||
$methods[$key] = new Kodoc_Method($this->class->name, $method->name);
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
protected function _method_sort($a,$b)
|
||||
{
|
||||
/*
|
||||
echo kohana::debug('a is '.$a->class.'::'.$a->name,'b is '.$b->class.'::'.$b->name,
|
||||
'are the classes the same?',$a->class == $b->class,'if they are, the result is:',strcmp($a->name,$b->name),
|
||||
'is a this class?',$a->name == $this->class->name,-1,
|
||||
'is b this class?',$b->name == $this->class->name,1,
|
||||
'otherwise, the result is:',strcmp($a->class,$b->class)
|
||||
);
|
||||
*/
|
||||
|
||||
|
||||
// If both methods are defined in the same class, just compare the method names
|
||||
if ($a->class == $b->class)
|
||||
return strcmp($a->name,$b->name);
|
||||
|
||||
// If one of them was declared by this class, it needs to be on top
|
||||
if ($a->name == $this->class->name)
|
||||
return -1;
|
||||
if ($b->name == $this->class->name)
|
||||
return 1;
|
||||
|
||||
// Otherwise, get the parents of each methods declaring class, then compare which function has more "ancestors"
|
||||
$adepth = 0;
|
||||
$bdepth = 0;
|
||||
|
||||
$parent = $a->getDeclaringClass();
|
||||
do
|
||||
{
|
||||
$adepth++;
|
||||
}
|
||||
while ($parent = $parent->getParentClass());
|
||||
|
||||
$parent = $b->getDeclaringClass();
|
||||
do
|
||||
{
|
||||
$bdepth++;
|
||||
}
|
||||
while ($parent = $parent->getParentClass());
|
||||
|
||||
return $bdepth - $adepth;
|
||||
}
|
||||
|
||||
} // End Kodac_Class
|
@@ -0,0 +1,169 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Custom Markdown parser for Kohana documentation.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
|
||||
|
||||
/**
|
||||
* @var string base url for links
|
||||
*/
|
||||
public static $base_url = '';
|
||||
|
||||
/**
|
||||
* @var string base url for images
|
||||
*/
|
||||
public static $image_url = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// doImage is 10, add image url just before
|
||||
$this->span_gamut['doImageURL'] = 9;
|
||||
|
||||
// doLink is 20, add base url just before
|
||||
$this->span_gamut['doBaseURL'] = 19;
|
||||
|
||||
// Add API links
|
||||
$this->span_gamut['doAPI'] = 90;
|
||||
|
||||
// Add note spans last
|
||||
$this->span_gamut['doNotes'] = 100;
|
||||
|
||||
// Parse Kohana view inclusions at the very end
|
||||
$this->document_gamut['doIncludeViews'] = 100;
|
||||
|
||||
// PHP4 makes me sad.
|
||||
parent::MarkdownExtra_Parser();
|
||||
}
|
||||
|
||||
public function doIncludeViews($text)
|
||||
{
|
||||
if (preg_match_all('/{{([^\s{}]++)}}/', $text, $matches, PREG_SET_ORDER))
|
||||
{
|
||||
$replace = array();
|
||||
|
||||
foreach ($matches as $set)
|
||||
{
|
||||
list($search, $view) = $set;
|
||||
|
||||
try
|
||||
{
|
||||
$replace[$search] = View::factory($view)->render();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
ob_start();
|
||||
|
||||
// Capture the exception handler output and insert it instead
|
||||
Kohana::exception_handler($e);
|
||||
|
||||
$replace[$search] = ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
$text = strtr($text, $replace);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the current base url to all local links.
|
||||
*
|
||||
* [filesystem](about.filesystem "Optional title")
|
||||
*
|
||||
* @param string span text
|
||||
* @return string
|
||||
*/
|
||||
public function doBaseURL($text)
|
||||
{
|
||||
// URLs containing "://" are left untouched
|
||||
return preg_replace('~(?<!!)(\[.+?\]\()(?!\w++://)([^#]\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$base_url.'$2', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the current base url to all local images.
|
||||
*
|
||||
* 
|
||||
*
|
||||
* @param string span text
|
||||
* @return string
|
||||
*/
|
||||
public function doImageURL($text)
|
||||
{
|
||||
// URLs containing "://" are left untouched
|
||||
return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$image_url.'$2', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses links to the API browser.
|
||||
*
|
||||
* [Class_Name] or [Class::$property]
|
||||
*
|
||||
* @param string span text
|
||||
* @return string
|
||||
*/
|
||||
public function doAPI($text)
|
||||
{
|
||||
return preg_replace_callback('/\[([a-z_]++(?:::\$?[a-z_]++)?)\]/i', array($this, '_convert_api_link'), $text);
|
||||
}
|
||||
|
||||
public function _convert_api_link($matches)
|
||||
{
|
||||
static $route;
|
||||
|
||||
if ($route === NULL)
|
||||
{
|
||||
$route = Route::get('docs/api');
|
||||
}
|
||||
|
||||
$link = $matches[1];
|
||||
|
||||
if (strpos($link, '::'))
|
||||
{
|
||||
// Split the class and method
|
||||
list($class, $method) = explode('::', $link, 2);
|
||||
|
||||
if ($method[0] === '$')
|
||||
{
|
||||
// Class property, not method
|
||||
$method = 'property:'.substr($method, 1);
|
||||
}
|
||||
|
||||
// Add the id symbol to the method
|
||||
$method = '#'.$method;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Class with no method
|
||||
$class = $link;
|
||||
$method = NULL;
|
||||
}
|
||||
|
||||
return HTML::anchor($route->uri(array('class' => $class)).$method, $link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap notes in the applicable markup. Notes can contain single newlines.
|
||||
*
|
||||
* [!!] Remember the milk!
|
||||
*
|
||||
* @param string span text
|
||||
* @return string
|
||||
*/
|
||||
public function doNotes($text)
|
||||
{
|
||||
if ( ! preg_match('/^\[!!\]\s*+(.+?)(?=\n{2,}|$)/s', $text, $match))
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
|
||||
return $this->hashBlock('<p class="note">'.$match[1].'</p>');
|
||||
}
|
||||
|
||||
} // End Kodoc_Markdown
|
@@ -0,0 +1,141 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Class method documentation generator.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc_Method extends Kodoc {
|
||||
|
||||
/**
|
||||
* @var ReflectionMethod The ReflectionMethod for this class
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* @var array array of Kodoc_Method_Param
|
||||
*/
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* @var array the things this function can return
|
||||
*/
|
||||
public $return = array();
|
||||
|
||||
/**
|
||||
* @var string the source code for this function
|
||||
*/
|
||||
public $source;
|
||||
|
||||
public function __construct($class, $method)
|
||||
{
|
||||
$this->method = new ReflectionMethod($class, $method);
|
||||
|
||||
$this->class = $parent = $this->method->getDeclaringClass();
|
||||
|
||||
if ($modifiers = $this->method->getModifiers())
|
||||
{
|
||||
$this->modifiers = '<small>'.implode(' ', Reflection::getModifierNames($modifiers)).'</small> ';
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if ($parent->hasMethod($method) AND $comment = $parent->getMethod($method)->getDocComment())
|
||||
{
|
||||
// Found a description for this method
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ($parent = $parent->getParentClass());
|
||||
|
||||
list($this->description, $tags) = Kodoc::parse($comment);
|
||||
|
||||
if ($file = $this->class->getFileName())
|
||||
{
|
||||
$this->source = Kodoc::source($file, $this->method->getStartLine(), $this->method->getEndLine());
|
||||
}
|
||||
|
||||
if (isset($tags['param']))
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($this->method->getParameters() as $i => $param)
|
||||
{
|
||||
$param = new Kodoc_Method_Param(array($this->method->class,$this->method->name),$i);
|
||||
|
||||
if (isset($tags['param'][$i]))
|
||||
{
|
||||
preg_match('/^(\S+)(?:\s*(?:\$'.$param->name.'\s*)?(.+))?$/', $tags['param'][$i], $matches);
|
||||
|
||||
$param->type = $matches[1];
|
||||
|
||||
if (isset($matches[2]))
|
||||
{
|
||||
$param->description = $matches[2];
|
||||
}
|
||||
}
|
||||
$params[] = $param;
|
||||
}
|
||||
|
||||
$this->params = $params;
|
||||
|
||||
unset($tags['param']);
|
||||
}
|
||||
|
||||
if (isset($tags['return']))
|
||||
{
|
||||
foreach ($tags['return'] as $return)
|
||||
{
|
||||
if (preg_match('/^(\S*)(?:\s*(.+?))?$/', $return, $matches))
|
||||
{
|
||||
$this->return[] = array($matches[1], isset($matches[2]) ? $matches[2] : '');
|
||||
}
|
||||
}
|
||||
|
||||
unset($tags['return']);
|
||||
}
|
||||
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
public function params_short()
|
||||
{
|
||||
$out = '';
|
||||
$required = TRUE;
|
||||
$first = TRUE;
|
||||
foreach ($this->params as $param)
|
||||
{
|
||||
if ($required AND $param->default AND $first)
|
||||
{
|
||||
$out .= '[ '.$param;
|
||||
$required = FALSE;
|
||||
$first = FALSE;
|
||||
}
|
||||
elseif ($required AND $param->default)
|
||||
{
|
||||
$out .= '[, '.$param;
|
||||
$required = FALSE;
|
||||
}
|
||||
elseif ($first)
|
||||
{
|
||||
$out .= $param;
|
||||
$first = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= ', '.$param;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $required)
|
||||
{
|
||||
$out .= '] ';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
} // End Kodoc_Method
|
@@ -0,0 +1,101 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Class method parameter documentation generator.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc_Method_Param extends Kodoc {
|
||||
|
||||
/**
|
||||
* @var object ReflectionParameter for this property
|
||||
*/
|
||||
public $param;
|
||||
|
||||
/**
|
||||
* @var string name of this var
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string variable type, retrieved from the comment
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var string default value of this param
|
||||
*/
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* @var string description of this parameter
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @var boolean is the parameter passed by reference?
|
||||
*/
|
||||
public $reference = FALSE;
|
||||
|
||||
/**
|
||||
* @var boolean is the parameter optional?
|
||||
*/
|
||||
public $optional = FALSE;
|
||||
|
||||
public function __construct($method, $param)
|
||||
{
|
||||
$this->param = new ReflectionParameter($method, $param);
|
||||
|
||||
$this->name = $this->param->name;
|
||||
|
||||
if ($this->param->isDefaultValueAvailable())
|
||||
{
|
||||
$this->default = Kohana::dump($this->param->getDefaultValue());
|
||||
}
|
||||
|
||||
if ($this->param->isPassedByReference())
|
||||
{
|
||||
$this->reference = TRUE;
|
||||
}
|
||||
|
||||
if ($this->param->isOptional())
|
||||
{
|
||||
$this->optional = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$display = '';
|
||||
|
||||
if ($this->type)
|
||||
{
|
||||
$display .= '<small>'.$this->type.'</small> ';
|
||||
}
|
||||
|
||||
if ($this->reference)
|
||||
{
|
||||
$display .= '<small><abbr title="passed by reference">&</abbr></small> ';
|
||||
}
|
||||
|
||||
if ($this->description)
|
||||
{
|
||||
$display .= '<span class="param" title="'.$this->description.'">$'.$this->name.'</span> ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$display .= '$'.$this->name.' ';
|
||||
}
|
||||
|
||||
if ($this->default)
|
||||
{
|
||||
$display .= '<small>= '.$this->default.'</small> ';
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
} // End Kodoc_Method_Param
|
@@ -0,0 +1,36 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* [!!] This class, or a class parent, could not be found or loaded. This could
|
||||
* be caused by a missing module or other dependancy. The documentation for
|
||||
* class will not be complete!
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Undocumented
|
||||
* @author Kohana Team
|
||||
* @since 3.0.7
|
||||
*/
|
||||
abstract class Kohana_Kodoc_Missing {
|
||||
|
||||
/**
|
||||
* Creates classes when they are otherwise not found.
|
||||
*
|
||||
* Kodoc::create_class('ThisClassDoesNotExist');
|
||||
*
|
||||
* [!!] All classes created will extend [Kodoc_Missing].
|
||||
*
|
||||
* @param string class name
|
||||
* @return boolean
|
||||
* @since 3.0.7
|
||||
*/
|
||||
public static function create_class($class)
|
||||
{
|
||||
if ( ! class_exists($class))
|
||||
{
|
||||
// Create a new missing class
|
||||
eval("class {$class} extends Kodoc_Missing {}");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} // End Kohana_Kodoc_Missing
|
@@ -0,0 +1,67 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Class property documentation generator.
|
||||
*
|
||||
* @package Kohana/Userguide
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Kodoc_Property extends Kodoc {
|
||||
|
||||
/**
|
||||
* @var object ReflectionProperty
|
||||
*/
|
||||
public $property;
|
||||
|
||||
/**
|
||||
* @var string modifiers: public, private, static, etc
|
||||
*/
|
||||
public $modifiers = 'public';
|
||||
|
||||
/**
|
||||
* @var string variable type, retrieved from the comment
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var string value of the property
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct($class, $property)
|
||||
{
|
||||
$property = new ReflectionProperty($class, $property);
|
||||
|
||||
list($description, $tags) = Kodoc::parse($property->getDocComment());
|
||||
|
||||
$this->description = $description;
|
||||
|
||||
if ($modifiers = $property->getModifiers())
|
||||
{
|
||||
$this->modifiers = '<small>'.implode(' ', Reflection::getModifierNames($modifiers)).'</small> ';
|
||||
}
|
||||
|
||||
if (isset($tags['var']))
|
||||
{
|
||||
if (preg_match('/^(\S*)(?:\s*(.+?))?$/', $tags['var'][0], $matches))
|
||||
{
|
||||
$this->type = $matches[1];
|
||||
|
||||
if (isset($matches[2]))
|
||||
{
|
||||
$this->description = $matches[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->property = $property;
|
||||
|
||||
if ($property->isStatic())
|
||||
{
|
||||
$this->value = Kohana::debug($property->getValue($class));
|
||||
}
|
||||
}
|
||||
|
||||
} // End Kodoc_Property
|
Reference in New Issue
Block a user