directory restructured
This commit is contained in:
parent
daec4cdf66
commit
723dc3afba
@ -9,10 +9,7 @@
|
|||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {
|
"psr-0": {
|
||||||
"Rails\\": ""
|
"Rails\\": "lib/Rails"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"vendor-dir": "./.."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
85
lib/Rails/ActionController/ActionController.php
Executable file
85
lib/Rails/ActionController/ActionController.php
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\ExceptionHandler;
|
||||||
|
use Rails\ActionController\UrlFor;
|
||||||
|
use Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
abstract class ActionController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Helps with _run_filters, which is protected.
|
||||||
|
*/
|
||||||
|
static public function run_filters_for(ApplicationController $ctrlr, $type)
|
||||||
|
{
|
||||||
|
$ctrlr->run_filters($type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function load_exception_handler($class_name, $status)
|
||||||
|
{
|
||||||
|
// $path = Rails::config()->paths->controllers;
|
||||||
|
// $file = $path . '/' . Rails::services()->get('inflector')->underscore($class_name) . '.php';
|
||||||
|
// if (!is_file($file))
|
||||||
|
// throw new ActionController_Exception(sprintf("File for exception handler controller not found. Searched for %s", $file));
|
||||||
|
// require $file;
|
||||||
|
|
||||||
|
// if (!class_exists($class_name, false))
|
||||||
|
// throw new ActionController_Exception(sprintf("Class for exception handler controller not found. File: %s", $file));
|
||||||
|
$handler = new $class_name();
|
||||||
|
if (!$handler instanceof ExceptionHandler) {
|
||||||
|
throw new ActionController_Exception(sprintf("Exception handler class %s must extend %s. File: %s", $class_name, "ActionController\ExceptionHandler", $file));
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler->class_name = $class_name;
|
||||||
|
$handler->setStatus((int)$status);
|
||||||
|
return $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function params($name = null, $val = null)
|
||||||
|
{
|
||||||
|
if ($name)
|
||||||
|
$this->_dispatcher()->parameters()->$name = $val;
|
||||||
|
return $this->_dispatcher()->parameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request()
|
||||||
|
{
|
||||||
|
return $this->_dispatcher()->request();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function response()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function session()
|
||||||
|
{
|
||||||
|
return $this->_dispatcher()->session();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves Cookies instance, retrieves a cookie or
|
||||||
|
* sets a cookie.
|
||||||
|
*/
|
||||||
|
public function cookies($name = null, $val = null, array $params = array())
|
||||||
|
{
|
||||||
|
$num = func_num_args();
|
||||||
|
|
||||||
|
if (!$num)
|
||||||
|
return $this->_dispatcher()->response()->cookies();
|
||||||
|
elseif ($num == 1)
|
||||||
|
return $this->_dispatcher()->response()->cookies()->get($name);
|
||||||
|
else {
|
||||||
|
if ($val === null)
|
||||||
|
$this->_dispatcher()->response()->cookies()->delete($name);
|
||||||
|
else
|
||||||
|
return $this->_dispatcher()->response()->cookies()->add($name, $val, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _dispatcher()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher();
|
||||||
|
}
|
||||||
|
}
|
808
lib/Rails/ActionController/Base.php
Executable file
808
lib/Rails/ActionController/Base.php
Executable file
@ -0,0 +1,808 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use Closure;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionMethod;
|
||||||
|
use ReflectionException;
|
||||||
|
use ApplicationController;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\ActionController;
|
||||||
|
use Rails\ActionController\ExceptionHandler;
|
||||||
|
use Rails\ActionView;
|
||||||
|
use Rails\Routing\Traits\NamedPathAwareTrait;
|
||||||
|
use Rails\Routing\UrlFor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is expected to be extended by the ApplicationController
|
||||||
|
* class, and that one is expected to be extended by the current controller's class.
|
||||||
|
* There can't be other extensions for now as they might cause
|
||||||
|
* unexpected results.
|
||||||
|
*
|
||||||
|
* ApplicationController class will be instantiated so its _init()
|
||||||
|
* method is called, because it is supposed to be called always, regardless
|
||||||
|
* the actual controller.
|
||||||
|
*/
|
||||||
|
abstract class Base extends ActionController
|
||||||
|
{
|
||||||
|
use NamedPathAwareTrait;
|
||||||
|
|
||||||
|
const CONTENT_TYPE_JSON = 'application/json';
|
||||||
|
|
||||||
|
const CONTENT_TYPE_XML = 'application/xml';
|
||||||
|
|
||||||
|
const DEFAULT_REDIRECT_STATUS = 302;
|
||||||
|
|
||||||
|
const APP_CONTROLLER_CLASS = 'ApplicationController';
|
||||||
|
|
||||||
|
static private $RENDER_OPTIONS = [
|
||||||
|
'action','template', 'lambda', 'partial', 'json', 'xml', 'text', 'inline', 'file', 'nothing'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see respondWith()
|
||||||
|
*/
|
||||||
|
private $respondTo = [];
|
||||||
|
|
||||||
|
private $layout;
|
||||||
|
|
||||||
|
private $actionRan = false;
|
||||||
|
|
||||||
|
# Must not include charset.
|
||||||
|
private $contentType;
|
||||||
|
|
||||||
|
private $charset;
|
||||||
|
|
||||||
|
private $status = 200;
|
||||||
|
|
||||||
|
private
|
||||||
|
/**
|
||||||
|
* Variables set to the controller itself will be stored
|
||||||
|
* here through __set(), and will be passed to the view.
|
||||||
|
*
|
||||||
|
* @var stdClass
|
||||||
|
* @see vars()
|
||||||
|
*/
|
||||||
|
$locals,
|
||||||
|
|
||||||
|
$_array_names = [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra actions to run according to request format.
|
||||||
|
*
|
||||||
|
* This accepts the following params:
|
||||||
|
* Null: Nothing has been set (default).
|
||||||
|
* True: Can respond to format, no action needed.
|
||||||
|
* Closure: Can respond to format, run Closure.
|
||||||
|
* False: Can't respond to format. Render Nothing with 406.
|
||||||
|
*/
|
||||||
|
$_respond_action,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default variable to respond with.
|
||||||
|
*
|
||||||
|
* @see respond_with()
|
||||||
|
*/
|
||||||
|
$_respond_with = [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the render parameters.
|
||||||
|
*
|
||||||
|
* @see render_params()
|
||||||
|
* @see _render()
|
||||||
|
* @see _redirect_to()
|
||||||
|
* @see _set_response_params()
|
||||||
|
*/
|
||||||
|
$_response_params = [],
|
||||||
|
|
||||||
|
$redirect_params,
|
||||||
|
$render_params,
|
||||||
|
|
||||||
|
$_response_extra_params = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReflectionClass for ApplicationController.
|
||||||
|
*/
|
||||||
|
private $appControllerRefls = [];
|
||||||
|
|
||||||
|
private $selfRefl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children classes shouldn't override __construct(),
|
||||||
|
* they should override init() instead.
|
||||||
|
*
|
||||||
|
* The classes "ApplicationController" are practically abstract classes.
|
||||||
|
* Some methods declared on them (init() and filters()) will be bound to the
|
||||||
|
* actual controller class and executed.
|
||||||
|
* This will happen with any class called "ApplicationController"
|
||||||
|
* under any namespace.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$class = get_called_class();
|
||||||
|
|
||||||
|
if (!$this->isAppController($class)) {
|
||||||
|
$this->locals = new stdClass();
|
||||||
|
$this->selfRefl = new ReflectionClass($class);
|
||||||
|
|
||||||
|
if (!$this instanceof ExceptionHandler) {
|
||||||
|
$this->_set_default_layout();
|
||||||
|
$reflection = $this->selfRefl;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$parent = $reflection->getParentClass();
|
||||||
|
if ($this->isAppController($parent->getName())) {
|
||||||
|
$this->appControllerRefls[] = $parent;
|
||||||
|
} elseif ($parent->getName() == __CLASS__) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$reflection = $parent;
|
||||||
|
}
|
||||||
|
$this->appControllerRefls = array_reverse($this->appControllerRefls);
|
||||||
|
$this->run_initializers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $val)
|
||||||
|
{
|
||||||
|
$this->setLocal($prop, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($prop)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($prop, (array)$this->locals)) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Trying to get undefined local '%s'", $prop)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this->locals->$prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $params)
|
||||||
|
{
|
||||||
|
if ($this->isNamedPathMethod($method)) {
|
||||||
|
return $this->getNamedPath($method, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception\BadMethodCallException(
|
||||||
|
sprintf("Called to unknown method: %s", $method)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function response_params()
|
||||||
|
{
|
||||||
|
return $this->_response_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function responded()
|
||||||
|
{
|
||||||
|
return $this->render_params || $this->redirect_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocal($name, $value)
|
||||||
|
{
|
||||||
|
if (is_array($value)) {
|
||||||
|
$this->_array_names[] = $name;
|
||||||
|
$this->$name = $value;
|
||||||
|
} else {
|
||||||
|
$this->locals->$name = $value;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function locals()
|
||||||
|
{
|
||||||
|
foreach ($this->_array_names as $prop_name)
|
||||||
|
$this->locals->$prop_name = $this->$prop_name;
|
||||||
|
return $this->locals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function vars()
|
||||||
|
{
|
||||||
|
return $this->locals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut
|
||||||
|
*/
|
||||||
|
public function I18n()
|
||||||
|
{
|
||||||
|
return Rails::services()->get('i18n');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_name()
|
||||||
|
{
|
||||||
|
return Rails::services()->get('inflector')->camelize(Rails::application()->dispatcher()->router()->route()->action, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run_request_action()
|
||||||
|
{
|
||||||
|
$action = $this->action_name();
|
||||||
|
|
||||||
|
if (!$this->_action_method_exists($action) && !$this->_view_file_exists()) {
|
||||||
|
throw new Exception\UnknownActionException(
|
||||||
|
sprintf("The action '%s' could not be found for %s", $action, get_called_class())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->runFilters('before');
|
||||||
|
/**
|
||||||
|
* Check if response params where set by the
|
||||||
|
* before filters.
|
||||||
|
*/
|
||||||
|
if (!$this->responded()) {
|
||||||
|
if ($this->_action_method_exists($action)) {
|
||||||
|
$this->actionRan = true;
|
||||||
|
$this->runAction($action);
|
||||||
|
}
|
||||||
|
$this->runFilters('after');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->redirect_params) {
|
||||||
|
$this->_set_redirection();
|
||||||
|
} else {
|
||||||
|
$this->_parse_response_params();
|
||||||
|
$this->_create_response_body();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionRan()
|
||||||
|
{
|
||||||
|
return $this->actionRan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method was created so it can be extended in
|
||||||
|
* the controllers with the solely purpose of
|
||||||
|
* customizing the handle of Exceptions.
|
||||||
|
*/
|
||||||
|
protected function runAction($action)
|
||||||
|
{
|
||||||
|
$this->$action();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds view helpers to the load list.
|
||||||
|
* Accepts one or many strings.
|
||||||
|
*/
|
||||||
|
public function helper()
|
||||||
|
{
|
||||||
|
ActionView\ViewHelpers::addAppHelpers(func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to format
|
||||||
|
*
|
||||||
|
* Sets to which formats the action will respond.
|
||||||
|
* It accepts a list of methods that will be called if the
|
||||||
|
* request matches the format.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* $this->respondTo(array(
|
||||||
|
* 'html',
|
||||||
|
* 'xml' => array(
|
||||||
|
* '_some_method' => array($param_1, $param_2),
|
||||||
|
* '_render' => array(array('xml' => $obj), array('status' => 403))
|
||||||
|
* )
|
||||||
|
* ));
|
||||||
|
*
|
||||||
|
* Note: The way this function receives its parameters is because we can't use Closures,
|
||||||
|
* due to the protected visibility of the methods such as _render().
|
||||||
|
*
|
||||||
|
* In the case above, it's stated that the action is able to respond to html and xml.
|
||||||
|
* In the case of an html request, no further action is needed to respond; therefore,
|
||||||
|
* we just list the 'html' format there.
|
||||||
|
* In the case of an xml request, the controller will call _some_method($param_1, $param_2)
|
||||||
|
* then it will call the _render() method, giving it the variable with which it will respond
|
||||||
|
* (an ActiveRecord_Base object, an array, etc), and setting the status to 403.
|
||||||
|
* Any request with a format not specified here will be responded with a 406 HTTP status code.
|
||||||
|
*
|
||||||
|
* By default all requests respond to xml, json and html. If the action receives a json
|
||||||
|
* request for example, but no data is set to respond with, the dispatcher will look for
|
||||||
|
* the .$format.php file in the views (in this case, .json.php). If the file is missing,
|
||||||
|
* which actually is expected to happen, a Dispatcher_TemplateMissing exception will be
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @see respond_with()
|
||||||
|
*/
|
||||||
|
public function respondTo($responses)
|
||||||
|
{
|
||||||
|
$format = $this->request()->format();
|
||||||
|
|
||||||
|
foreach ($responses as $fmt => $action) {
|
||||||
|
if (is_int($fmt)) {
|
||||||
|
$fmt = $action;
|
||||||
|
$action = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fmt !== $format)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ($action) {
|
||||||
|
if (!$action instanceof Closure) {
|
||||||
|
throw new Exception\InvalidArgumentException(sprinft('Only closure can be passed to respondTo, %s passed', gettype($action)));
|
||||||
|
}
|
||||||
|
$action();
|
||||||
|
} else {
|
||||||
|
$action = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_respond_action = $action;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request format is not acceptable.
|
||||||
|
* Set to render nothing with 406 status.
|
||||||
|
*/
|
||||||
|
Rails::log()->message("406 Not Acceptable");
|
||||||
|
$this->render(array('nothing' => true), array('status' => 406));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function respondWith($var)
|
||||||
|
{
|
||||||
|
$format = $this->request()->format();
|
||||||
|
|
||||||
|
if (!in_array($format, $this->respondTo)) {
|
||||||
|
/**
|
||||||
|
* The request format is not acceptable.
|
||||||
|
* Set to render nothing with 406 status.
|
||||||
|
*/
|
||||||
|
$this->render(array('nothing' => true), array('status' => 406));
|
||||||
|
} else {
|
||||||
|
$responses = [];
|
||||||
|
foreach ($this->respondTo as $format) {
|
||||||
|
if ($format == 'html') {
|
||||||
|
$responses[] = 'html';
|
||||||
|
} else {
|
||||||
|
$responses[$format] = function() use ($var, $format) {
|
||||||
|
$this->render([$format => $var]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->respondTo($responses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets layout value.
|
||||||
|
*/
|
||||||
|
public function layout($value = null)
|
||||||
|
{
|
||||||
|
if (null === $value)
|
||||||
|
return $this->layout;
|
||||||
|
else
|
||||||
|
$this->layout = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLayout($value)
|
||||||
|
{
|
||||||
|
$this->layout = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setRespondTo(array $respondTo)
|
||||||
|
{
|
||||||
|
$this->respondTo = $respondTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function filters()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render($render_params)
|
||||||
|
{
|
||||||
|
if ($this->responded()) {
|
||||||
|
throw new Exception\DoubleRenderException('Can only render or redirect once per action.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->render_params = $render_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a redirection.
|
||||||
|
* Accepts ['status' => $http_status] among the $redirect_params.
|
||||||
|
*/
|
||||||
|
protected function redirectTo($redirect_params)
|
||||||
|
{
|
||||||
|
if ($this->responded()) {
|
||||||
|
throw new Exception\DoubleRenderException('Can only render or redirect once per action.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect_params = $redirect_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For now we're only expecting one controller that extends ApplicationController,
|
||||||
|
* that extends ActionController_Base.
|
||||||
|
* This could change in the future (using Reflections) so there could be more classes
|
||||||
|
* extending down to ApplicationController > ActionController_Base
|
||||||
|
*/
|
||||||
|
protected function runFilters($type)
|
||||||
|
{
|
||||||
|
$closures = $this->getAppControllersMethod('filters');
|
||||||
|
|
||||||
|
if ($closures) {
|
||||||
|
$filters = [];
|
||||||
|
foreach ($closures as $closure) {
|
||||||
|
$filters = array_merge_recursive($closure(), $filters);
|
||||||
|
}
|
||||||
|
$filters = array_merge_recursive($filters, $this->filters());
|
||||||
|
} else {
|
||||||
|
$filters = $this->filters();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($filters[$type])) {
|
||||||
|
/**
|
||||||
|
* We have to filter duped methods. We can't use array_unique
|
||||||
|
* because the the methods could be like 'method_name' => [ 'only' => [ actions ... ] ]
|
||||||
|
* and that will generate "Array to string conversion" error.
|
||||||
|
*/
|
||||||
|
$ranMethods = [];
|
||||||
|
|
||||||
|
foreach ($filters[$type] as $methodName => $params) {
|
||||||
|
if (!is_array($params)) {
|
||||||
|
$methodName = $params;
|
||||||
|
$params = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->canRunFilterMethod($params, $type) && !in_array($methodName, $ranMethods)) {
|
||||||
|
$this->$methodName();
|
||||||
|
/**
|
||||||
|
* Before-filters may set response params. Running filters stop if one of them does.
|
||||||
|
*/
|
||||||
|
if ($type == 'before' && $this->responded()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$ranMethods[] = $methodName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setStatus($status)
|
||||||
|
{
|
||||||
|
$this->status = $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _action_method_exists($action)
|
||||||
|
{
|
||||||
|
$method_exists = false;
|
||||||
|
$called_class = get_called_class();
|
||||||
|
$refl = new ReflectionClass($called_class);
|
||||||
|
|
||||||
|
if ($refl->hasMethod($action)) {
|
||||||
|
$method = $refl->getMethod($action);
|
||||||
|
if ($method->getDeclaringClass()->getName() == $called_class && $method->isPublic())
|
||||||
|
$method_exists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $method_exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function urlFor($params)
|
||||||
|
{
|
||||||
|
$urlfor = new UrlFor($params);
|
||||||
|
return $urlfor->url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the method for the requested action doesn't exist in
|
||||||
|
* the controller, it's checked if the view file exists.
|
||||||
|
*/
|
||||||
|
private function _view_file_exists()
|
||||||
|
{
|
||||||
|
$route = Rails::application()->dispatcher()->router()->route();
|
||||||
|
/**
|
||||||
|
* Build a simple path for the view file, as there's no support for
|
||||||
|
* stuff like modules.
|
||||||
|
* Note that the extension is PHP, there's no support for different
|
||||||
|
* request formats.
|
||||||
|
*/
|
||||||
|
$base_path = Rails::config()->paths->views;
|
||||||
|
$view_path = $base_path . '/' . $route->path() . '.php';
|
||||||
|
return is_file($view_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _set_default_layout()
|
||||||
|
{
|
||||||
|
$this->layout(Rails::application()->config()->action_view->layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canRunFilterMethod(array $params = [], $filter_type)
|
||||||
|
{
|
||||||
|
$action = Rails::services()->get('inflector')->camelize($this->request()->action(), false);
|
||||||
|
|
||||||
|
if (isset($params['only']) && !in_array($action, $params['only'])) {
|
||||||
|
return false;
|
||||||
|
} elseif (isset($params['except']) && in_array($action, $params['except'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _parse_response_params()
|
||||||
|
{
|
||||||
|
if ($this->render_params) {
|
||||||
|
if (is_string($this->render_params)) {
|
||||||
|
$this->render_params = [ $this->render_params ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->render_params[0])) {
|
||||||
|
$param = $this->render_params[0];
|
||||||
|
unset($this->render_params[0]);
|
||||||
|
|
||||||
|
if ($param == 'nothing') {
|
||||||
|
$this->render_params['nothing'] = true;
|
||||||
|
} elseif (is_int(($pos = strpos($param, '/'))) && $pos > 0) {
|
||||||
|
$this->render_params['template'] = $param;
|
||||||
|
} elseif ($pos === 0 || strpos($param, ':') === 1) {
|
||||||
|
$this->render_params['file'] = $param;
|
||||||
|
if (!isset($this->render_params['layout'])) {
|
||||||
|
$this->render_params['layout'] = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->render_params['action'] = $param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else {
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// else {
|
||||||
|
// $route = Rails::application()->dispatcher()->router()->route();
|
||||||
|
// $this->render_params = ['action' => $route->action];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protected so it can be accessed by ExceptionHandler
|
||||||
|
*/
|
||||||
|
protected function _create_response_body()
|
||||||
|
{
|
||||||
|
$route = Rails::application()->dispatcher()->router()->route();
|
||||||
|
$render_type = false;
|
||||||
|
|
||||||
|
foreach (self::$RENDER_OPTIONS as $option) {
|
||||||
|
if (isset($this->render_params[$option])) {
|
||||||
|
$render_type = $option;
|
||||||
|
$main_param = $this->render_params[$option];
|
||||||
|
unset($this->render_params[$option]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$render_type) {
|
||||||
|
$render_type = 'action';
|
||||||
|
$main_param = $route->action;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $render_type = key($this->render_params);
|
||||||
|
// $main_param = array_shift($this->render_params);
|
||||||
|
|
||||||
|
|
||||||
|
if (isset($this->render_params['status']))
|
||||||
|
$this->status = $this->render_params['status'];
|
||||||
|
|
||||||
|
$class = null;
|
||||||
|
|
||||||
|
switch ($render_type) {
|
||||||
|
case 'action':
|
||||||
|
# Cut the 'Controller' part of the class name.
|
||||||
|
$path = Rails::services()->get('inflector')->underscore(substr(get_called_class(), 0, -10)) . '/' . $main_param . '.php';
|
||||||
|
|
||||||
|
// if ($route->namespaces())
|
||||||
|
// $path = implode('/', $route->namespaces()) . '/' . $path;
|
||||||
|
|
||||||
|
$main_param = $path;
|
||||||
|
# Fallthrough
|
||||||
|
|
||||||
|
case 'template':
|
||||||
|
$layout = !empty($this->render_params['layout']) ? $this->render_params['layout'] : $this->layout;
|
||||||
|
|
||||||
|
$ext = pathinfo($main_param, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
|
if (!$ext) {
|
||||||
|
$template_name = $main_param;
|
||||||
|
|
||||||
|
if ($this->request()->format() == 'html') {
|
||||||
|
$ext = 'php';
|
||||||
|
} else {
|
||||||
|
if ($this->request()->format() == 'xml')
|
||||||
|
$this->_response_params['is_xml'] = true;
|
||||||
|
$ext = [$this->request()->format(), 'php'];
|
||||||
|
|
||||||
|
$this->response()->headers()->contentType($this->request()->format());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$pinfo = pathinfo($main_param);
|
||||||
|
if ($sub_ext = pathinfo($pinfo['filename'], PATHINFO_EXTENSION)) {
|
||||||
|
$pinfo['filename'] = substr($pinfo['filename'], 0, -1 * (1 + strlen($pinfo['filename'])));
|
||||||
|
$ext = [$sub_ext, $ext];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_name = $pinfo['dirname'] . '/' . $pinfo['filename'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_response_params = [
|
||||||
|
'layout' => $layout,
|
||||||
|
'template_name' => $template_name,
|
||||||
|
'extension' => $ext
|
||||||
|
];
|
||||||
|
|
||||||
|
# Here we could choose a different responder according to extensions(?).
|
||||||
|
$class = 'Rails\ActionController\Response\Template';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lambda':
|
||||||
|
$class = 'Rails\ActionController\Response\Lambda';
|
||||||
|
$this->_response_params['lambda'] = $main_param;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'partial':
|
||||||
|
$class = 'Rails\ActionController\Response\Partial';
|
||||||
|
$this->_response_params['partial'] = $main_param;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'json':
|
||||||
|
$this->contentType = self::CONTENT_TYPE_JSON;
|
||||||
|
$this->_response_params = $main_param;
|
||||||
|
$class = "Rails\ActionController\Response\Json";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'xml':
|
||||||
|
$this->contentType = self::CONTENT_TYPE_XML;
|
||||||
|
array_unshift($this->_response_params, $main_param);
|
||||||
|
$class = "Rails\ActionController\Response\Xml";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
$this->response()->body($main_param);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'inline':
|
||||||
|
$this->_response_params['code'] = $main_param;
|
||||||
|
$this->_response_params['layout'] = $this->layout;
|
||||||
|
$class = "Rails\ActionController\Response\Inline";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'file':
|
||||||
|
$this->_response_params['file'] = $main_param;
|
||||||
|
$this->_response_params['layout'] = $this->layout ?: false;
|
||||||
|
$class = "Rails\ActionController\Response\File";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'nothing':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception\RuntimeException(sprintf("Invalid action render type '%s'", $render_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($class) {
|
||||||
|
$responder = new $class($this->_response_params);
|
||||||
|
$responder->render_view();
|
||||||
|
$this->response()->body($responder->get_contents());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _set_redirection()
|
||||||
|
{
|
||||||
|
$redirect_params = $this->redirect_params;
|
||||||
|
|
||||||
|
if (!is_array($redirect_params))
|
||||||
|
$redirect_params = [$redirect_params];
|
||||||
|
|
||||||
|
if (!empty($redirect_params['status'])) {
|
||||||
|
$status = $redirect_params['status'];
|
||||||
|
unset($redirect_params['status']);
|
||||||
|
} else {
|
||||||
|
$status = self::DEFAULT_REDIRECT_STATUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = Rails::application()->router()->urlFor($redirect_params);
|
||||||
|
|
||||||
|
$this->response()->headers()->location($url);
|
||||||
|
$this->response()->headers()->status($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs initializers for both the actual controller class
|
||||||
|
* and it's parent ApplicationController, if any.
|
||||||
|
*/
|
||||||
|
private function run_initializers()
|
||||||
|
{
|
||||||
|
$method_name = 'init';
|
||||||
|
$cn = get_called_class();
|
||||||
|
|
||||||
|
# Run ApplicationController's init method.
|
||||||
|
if ($inits = $this->getAppControllersMethod($method_name)) {
|
||||||
|
foreach ($inits as $init) {
|
||||||
|
$init = $init->bindTo($this);
|
||||||
|
$init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = $this->selfRefl->getMethod($method_name);
|
||||||
|
if ($method->getDeclaringClass()->getName() == $cn) {
|
||||||
|
$this->$method_name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through all the ApplicationControllers classes for a method,
|
||||||
|
* and returns them all.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getAppControllersMethod($methodName, $scope = '')
|
||||||
|
{
|
||||||
|
if ($this->appControllerRefls) {
|
||||||
|
$methods = [];
|
||||||
|
foreach ($this->appControllerRefls as $appRefl) {
|
||||||
|
if ($appRefl->hasMethod($methodName)) {
|
||||||
|
$method = $appRefl->getMethod($methodName);
|
||||||
|
|
||||||
|
if ($this->isAppController($method->getDeclaringClass()->getName())) {
|
||||||
|
if ($scope) {
|
||||||
|
$isScope = 'is' . ucfirst($scope);
|
||||||
|
if (!$method->$isScope()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$methods[] = $method->getClosure($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($methods) {
|
||||||
|
return $methods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setHeaders()
|
||||||
|
{
|
||||||
|
$headers = $this->response()->headers();
|
||||||
|
if (null === $this->charset)
|
||||||
|
$this->charset = Rails::application()->config()->action_controller->base->default_charset;
|
||||||
|
|
||||||
|
if (!$headers->contentType()) {
|
||||||
|
if (null === $this->contentType) {
|
||||||
|
$this->contentType = 'text/html';
|
||||||
|
}
|
||||||
|
$contentType = $this->contentType;
|
||||||
|
if ($this->charset)
|
||||||
|
$contentType .= '; charset=' . $this->charset;
|
||||||
|
|
||||||
|
$headers->setContentType($contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->response()->headers()->status($this->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isAppController($class)
|
||||||
|
{
|
||||||
|
return strpos($class, self::APP_CONTROLLER_CLASS) === (strlen($class) - strlen(self::APP_CONTROLLER_CLASS));
|
||||||
|
}
|
||||||
|
}
|
96
lib/Rails/ActionController/Cookie.php
Executable file
96
lib/Rails/ActionController/Cookie.php
Executable file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is expected to be used only by Rails_ActionDispatch_CookieJar
|
||||||
|
*/
|
||||||
|
class Cookie
|
||||||
|
{
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
protected $value;
|
||||||
|
|
||||||
|
protected $expire;
|
||||||
|
|
||||||
|
protected $path;
|
||||||
|
|
||||||
|
protected $domain;
|
||||||
|
|
||||||
|
protected $secure = false;
|
||||||
|
|
||||||
|
protected $httponly = false;
|
||||||
|
|
||||||
|
protected $raw = false;
|
||||||
|
|
||||||
|
public function __construct($name, $value, $expire = null, $path = null, $domain = null, $secure = false, $httponly = false, $raw = false)
|
||||||
|
{
|
||||||
|
$this->setDefaultValues();
|
||||||
|
|
||||||
|
$this->name = (string)$name;
|
||||||
|
$this->domain = (string)$domain;
|
||||||
|
$this->value = $value === null ? '' : $value;
|
||||||
|
$this->expire = $expire;
|
||||||
|
$this->path = $path;
|
||||||
|
$this->secure = $secure;
|
||||||
|
$this->httponly = $httponly;
|
||||||
|
$this->raw = $raw;
|
||||||
|
|
||||||
|
if (!$this->name) {
|
||||||
|
throw new Exception\InvalidArgumentException('Cookies must have a name');
|
||||||
|
}
|
||||||
|
if (preg_match("/[=,; \t\r\n\013\014]/", $this->name)) {
|
||||||
|
throw new Exception\InvalidArgumentException("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014 ({$this->name})");
|
||||||
|
}
|
||||||
|
if (is_string($this->expire)) {
|
||||||
|
$time = strtotime($this->expire);
|
||||||
|
if (!$time) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Invalid expiration time: %s", $time)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->expire = $time;
|
||||||
|
}
|
||||||
|
if ($this->expire !== null && !is_int($this->expire)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Cookie expiration time must be an integer, %s passed (%s)", gettype($this->expire), $this->expire)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($this->raw && preg_match("/[=,; \t\r\n\013\014]/", $this->value)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Raw cookie value cannot contain these characters: =,; \\t\\r\\n\\013\\014 (%s)", $this->value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!is_scalar($this->value)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Cookie value must be a scalar value, %s passed", gettype($this->value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function value()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set()
|
||||||
|
{
|
||||||
|
if ($this->raw) {
|
||||||
|
setrawcookie($this->name, $this->value, $this->expire, $this->path, $this->domain, $this->secure, $this->httponly);
|
||||||
|
} else {
|
||||||
|
setcookie($this->name, $this->value, $this->expire, $this->path, $this->domain, $this->secure, $this->httponly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setDefaultValues()
|
||||||
|
{
|
||||||
|
$config = Rails::application()->config()->cookies;
|
||||||
|
foreach ($config as $prop => $val) {
|
||||||
|
# Silently ignore unknown properties.
|
||||||
|
if (property_exists($this, $prop)) {
|
||||||
|
$this->$prop = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
lib/Rails/ActionController/Cookies.php
Executable file
94
lib/Rails/ActionController/Cookies.php
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will be available in controllers by
|
||||||
|
* calling '$this->cookies()'.
|
||||||
|
*
|
||||||
|
* Cookies can be set through this class. The cookies won't
|
||||||
|
* be actually sent to the browser until the controller
|
||||||
|
* finishes its work.
|
||||||
|
*
|
||||||
|
* Doing '$cookies->some_cookie' will call __get,
|
||||||
|
* which checks the $_COOKIE variable. In other words, it will
|
||||||
|
* check if the request sent a cookie named 'some_cookie'.
|
||||||
|
* If not, it will return null.
|
||||||
|
*
|
||||||
|
* To set a cookie, use add(), to remove them use remove().
|
||||||
|
* To check if a cookie was set by the controller, use in_jar().
|
||||||
|
*/
|
||||||
|
class Cookies
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Holds cookies that will be added at the
|
||||||
|
* end of the controller.
|
||||||
|
*/
|
||||||
|
private $jar = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To know if cookies were set or not.
|
||||||
|
*/
|
||||||
|
private $cookiesSet = false;
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if (isset($this->jar[$name]))
|
||||||
|
return $this->jar[$name]->value();
|
||||||
|
elseif (isset($_COOKIE[$name]))
|
||||||
|
return $_COOKIE[$name];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $params)
|
||||||
|
{
|
||||||
|
if (is_array($params)) {
|
||||||
|
if (isset($params['value'])) {
|
||||||
|
$value = $params['value'];
|
||||||
|
unset($params['value']);
|
||||||
|
} else {
|
||||||
|
$value = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $params;
|
||||||
|
$params = [];
|
||||||
|
}
|
||||||
|
$this->add($prop, $value, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($name, $value, array $params = array())
|
||||||
|
{
|
||||||
|
$p = array_merge($this->defaultParams(), $params);
|
||||||
|
$this->jar[$name] = new Cookie($name, $value, $p['expires'], $p['path'], $p['domain'], $p['secure'], $p['httponly'], $p['raw']);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($name, array $params = [])
|
||||||
|
{
|
||||||
|
$this->add($name, '', array_merge($params, [
|
||||||
|
'expires' => time() - 172800
|
||||||
|
]));
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually sets cookie in headers.
|
||||||
|
*/
|
||||||
|
public function set()
|
||||||
|
{
|
||||||
|
if (!$this->cookiesSet) {
|
||||||
|
foreach ($this->jar as $c)
|
||||||
|
$c->set();
|
||||||
|
$this->cookiesSet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function defaultParams()
|
||||||
|
{
|
||||||
|
$defaults = \Rails::application()->config()->cookies->toArray();
|
||||||
|
if (!$defaults['path']) {
|
||||||
|
$defaults['path'] = \Rails::application()->router()->basePath() . '/';
|
||||||
|
}
|
||||||
|
return $defaults;
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActionController/Exception/BadMethodCallException.php
Executable file
6
lib/Rails/ActionController/Exception/BadMethodCallException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class BadMethodCallException extends \Rails\Exception\BadMethodCallException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionController/Exception/BodyAlreadySetException.php
Executable file
6
lib/Rails/ActionController/Exception/BodyAlreadySetException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class BodyAlreadySetException extends \Rails\Exception\LogicException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
7
lib/Rails/ActionController/Exception/DoubleRenderException.php
Executable file
7
lib/Rails/ActionController/Exception/DoubleRenderException.php
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class DoubleRenderException extends Rails\Exception\LogicException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
protected $title = 'Double Render Error';
|
||||||
|
}
|
6
lib/Rails/ActionController/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionController/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionController/Exception/InvalidArgumentException.php
Executable file
6
lib/Rails/ActionController/Exception/InvalidArgumentException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \Rails\Exception\InvalidArgumentException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionController/Exception/RuntimeException.php
Executable file
6
lib/Rails/ActionController/Exception/RuntimeException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
11
lib/Rails/ActionController/Exception/UnknownActionException.php
Executable file
11
lib/Rails/ActionController/Exception/UnknownActionException.php
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Exception;
|
||||||
|
|
||||||
|
class UnknownActionException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
protected $title = 'Unknown action';
|
||||||
|
|
||||||
|
protected $status = 404;
|
||||||
|
|
||||||
|
protected $skip_info = true;
|
||||||
|
}
|
50
lib/Rails/ActionController/ExceptionHandler.php
Executable file
50
lib/Rails/ActionController/ExceptionHandler.php
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic use:
|
||||||
|
* Create a class that extends this one.
|
||||||
|
* According to Exception or status, change the value of $template
|
||||||
|
* The system will render that template.
|
||||||
|
*/
|
||||||
|
abstract class ExceptionHandler extends Base
|
||||||
|
{
|
||||||
|
protected $exception;
|
||||||
|
|
||||||
|
protected $template = 'exception';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
switch ($this->status()) {
|
||||||
|
case 404:
|
||||||
|
$this->template = '404';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->template = '500';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleException(\Exception $e)
|
||||||
|
{
|
||||||
|
$this->exception = $e;
|
||||||
|
$this->setLayout(false);
|
||||||
|
|
||||||
|
$this->runAction("handle");
|
||||||
|
|
||||||
|
if (!$this->responded()) {
|
||||||
|
$this->render(['action' => $this->template]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_create_response_body();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionRan()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
81
lib/Rails/ActionController/Response.php
Executable file
81
lib/Rails/ActionController/Response.php
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController;
|
||||||
|
|
||||||
|
use Rails\ActionDispatch\ActionDispatch;
|
||||||
|
|
||||||
|
class Response extends ActionDispatch
|
||||||
|
{
|
||||||
|
private
|
||||||
|
/**
|
||||||
|
* ActionController_Cookies instance
|
||||||
|
*/
|
||||||
|
$cookies,
|
||||||
|
|
||||||
|
$body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to false, cookies and headers won't be send.
|
||||||
|
*/
|
||||||
|
protected $send_headers = true;
|
||||||
|
|
||||||
|
public function cookies()
|
||||||
|
{
|
||||||
|
if (!$this->cookies)
|
||||||
|
$this->cookies = new Cookies();
|
||||||
|
return $this->cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headers()
|
||||||
|
{
|
||||||
|
return \Rails::application()->dispatcher()->headers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function body($body = null)
|
||||||
|
{
|
||||||
|
if (null !== $body) {
|
||||||
|
$this->body = $body;
|
||||||
|
return $this;
|
||||||
|
} else {
|
||||||
|
return $this->body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendHeaders($value = null)
|
||||||
|
{
|
||||||
|
if ($value === null)
|
||||||
|
return $this->send_headers;
|
||||||
|
else {
|
||||||
|
$this->send_headers = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send_headers($value = null)
|
||||||
|
{
|
||||||
|
if ($value === null)
|
||||||
|
return $this->send_headers;
|
||||||
|
else {
|
||||||
|
$this->send_headers = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _respond()
|
||||||
|
{
|
||||||
|
$this->_send_headers();
|
||||||
|
echo $this->body;
|
||||||
|
$this->body = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _send_headers()
|
||||||
|
{
|
||||||
|
if ($this->send_headers) {
|
||||||
|
$this->headers()->send();
|
||||||
|
$this->cookies()->set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
lib/Rails/ActionController/Response/Base.php
Executable file
27
lib/Rails/ActionController/Response/Base.php
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails\ActionController;
|
||||||
|
|
||||||
|
abstract class Base extends ActionController\Response
|
||||||
|
{
|
||||||
|
public function __construct(array $params = array())
|
||||||
|
{
|
||||||
|
$this->_params = $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_view()
|
||||||
|
{
|
||||||
|
$this->_render_view();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_contents()
|
||||||
|
{
|
||||||
|
return $this->_print_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function _render_view();
|
||||||
|
|
||||||
|
abstract public function _print_view();
|
||||||
|
}
|
94
lib/Rails/ActionController/Response/Error.php
Executable file
94
lib/Rails/ActionController/Response/Error.php
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class also logs the errors.
|
||||||
|
*/
|
||||||
|
class Error extends Base
|
||||||
|
{
|
||||||
|
private
|
||||||
|
$_e,
|
||||||
|
$_buffer = '',
|
||||||
|
$_report;
|
||||||
|
|
||||||
|
public function __construct(\Exception $e, array $params)
|
||||||
|
{
|
||||||
|
$this->_params = $params;
|
||||||
|
$this->_e = $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
$buffer = '';
|
||||||
|
$this->_report = $this->_params['report'];
|
||||||
|
unset($this->_params['report']);
|
||||||
|
|
||||||
|
if (\Rails::application()->config()->consider_all_requests_local) {
|
||||||
|
$no_html = \Rails::cli();
|
||||||
|
|
||||||
|
if ($no_html) {
|
||||||
|
$buffer .= strip_tags($this->_report);
|
||||||
|
$buffer .= "\n";
|
||||||
|
} else {
|
||||||
|
$buffer .= $this->_header();
|
||||||
|
$buffer .= $this->_report;
|
||||||
|
$buffer .= $this->_footer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$file = \Rails::publicPath() . '/' . $this->_params['status'] . '.html';
|
||||||
|
if (is_file($file)) {
|
||||||
|
$buffer = file_get_contents($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_buffer = $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _header()
|
||||||
|
{
|
||||||
|
$h = <<<HEREDOC
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Exception caught</title>
|
||||||
|
<style>
|
||||||
|
body { background-color: #fff; color: #333; }
|
||||||
|
|
||||||
|
body, p, ol, ul, td {
|
||||||
|
font-family: helvetica, verdana, arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #eee;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.scroll {
|
||||||
|
max-height:400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a { color: #000; }
|
||||||
|
a:visited { color: #666; }
|
||||||
|
a:hover { color: #fff; background-color:#000; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
HEREDOC;
|
||||||
|
return $h;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _footer()
|
||||||
|
{
|
||||||
|
return "</body>\n</html>";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response\Exception;
|
||||||
|
|
||||||
|
class ActionNotFoundException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionController/Response/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionController/Response/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
7
lib/Rails/ActionController/Response/Exception/ViewNotFoundException.php
Executable file
7
lib/Rails/ActionController/Response/Exception/ViewNotFoundException.php
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response\Exception;
|
||||||
|
|
||||||
|
class ViewNotFoundException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
protected $title = "View not found";
|
||||||
|
}
|
28
lib/Rails/ActionController/Response/File.php
Executable file
28
lib/Rails/ActionController/Response/File.php
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
class File extends Base
|
||||||
|
{
|
||||||
|
private $_template;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
# Include helpers.
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
$layout = !empty($this->_params['layout']) ? $this->_params['layout'] : false;
|
||||||
|
|
||||||
|
$this->_template = new ActionView\Template($this->_params['file'], ['layout' => $layout]);
|
||||||
|
|
||||||
|
$this->_template->setLocals(\Rails::application()->controller()->vars());
|
||||||
|
|
||||||
|
$this->_template->renderContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_template->content();
|
||||||
|
}
|
||||||
|
}
|
26
lib/Rails/ActionController/Response/Inline.php
Executable file
26
lib/Rails/ActionController/Response/Inline.php
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
class Inline extends Base
|
||||||
|
{
|
||||||
|
private $_template;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
# Include helpers.
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
$layout = !empty($this->_params['layout']) ? $this->_params['layout'] : false;
|
||||||
|
# Create a template so we can call render_inline;
|
||||||
|
$this->_template = new ActionView\Template(['inline' => $this->_params['code']], ['layout' => $layout]);
|
||||||
|
$this->_template->setLocals(Rails::application()->controller()->vars());
|
||||||
|
$this->_template->renderContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_template->content();
|
||||||
|
}
|
||||||
|
}
|
55
lib/Rails/ActionController/Response/Json.php
Executable file
55
lib/Rails/ActionController/Response/Json.php
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
class Json extends Base
|
||||||
|
{
|
||||||
|
private $_json;
|
||||||
|
|
||||||
|
private $_header_params;
|
||||||
|
|
||||||
|
public function __construct($json)
|
||||||
|
{
|
||||||
|
$this->_json = $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
if ($this->_json instanceof \Rails\ActiveRecord\Base)
|
||||||
|
$this->_json = $this->_json->toJson();
|
||||||
|
elseif ($this->_json instanceof \Rails\ActiveRecord\Collection) {
|
||||||
|
$this->_json = $this->_json->toJson();
|
||||||
|
} elseif (isset($this->_json['json'])) {
|
||||||
|
if (!is_string($this->_json['json']))
|
||||||
|
$this->_json = json_encode($this->_json['json']);
|
||||||
|
else
|
||||||
|
$this->_json = $this->_json['json'];
|
||||||
|
} elseif (!is_string($this->_json)) {
|
||||||
|
if (is_array($this->_json)) {
|
||||||
|
$json = [];
|
||||||
|
foreach ($this->_json as $key => $val) {
|
||||||
|
$json[$key] = $this->_to_array($val);
|
||||||
|
}
|
||||||
|
$this->_json = $json;
|
||||||
|
}
|
||||||
|
$this->_json = json_encode($this->_json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _to_array($val)
|
||||||
|
{
|
||||||
|
if ($val instanceof \Rails\ActiveRecord\Collection) {
|
||||||
|
$json = [];
|
||||||
|
foreach ($val as $obj)
|
||||||
|
$json[] = $this->_to_array($obj);
|
||||||
|
return $json;
|
||||||
|
} elseif (is_object($val)) {
|
||||||
|
return (array)$val;
|
||||||
|
} else
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
}
|
25
lib/Rails/ActionController/Response/Lambda.php
Executable file
25
lib/Rails/ActionController/Response/Lambda.php
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
class Lambda extends Base
|
||||||
|
{
|
||||||
|
private $_template;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
# Include helpers.
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
$layout = !empty($this->_params['layout']) ? $this->_params['layout'] : false;
|
||||||
|
$this->_template = new ActionView\Template(['lambda' => $this->_params['lambda']], ['layout' => $layout]);
|
||||||
|
$this->_template->setLocals(\Rails::application()->controller()->locals());
|
||||||
|
$this->_template->renderContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_template->content();
|
||||||
|
}
|
||||||
|
}
|
29
lib/Rails/ActionController/Response/Partial.php
Executable file
29
lib/Rails/ActionController/Response/Partial.php
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
class ActionController_Response_Partial extends Base
|
||||||
|
{
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
$params = [$this->_params['partial']];
|
||||||
|
if (isset($this->_params['locals']))
|
||||||
|
$params = array_merge($params, [$this->_params['locals']]);
|
||||||
|
|
||||||
|
# Include helpers.
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
# Create a template so we can call render_partial.
|
||||||
|
# This shouldn't be done this way.
|
||||||
|
|
||||||
|
$template = new ActionView\Template([]);
|
||||||
|
|
||||||
|
$this->_body = call_user_func_array([$template, 'render_partial'], $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_body;
|
||||||
|
}
|
||||||
|
}
|
27
lib/Rails/ActionController/Response/Redirect.php
Executable file
27
lib/Rails/ActionController/Response/Redirect.php
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
class ActionController_Response_Redirect extends Base
|
||||||
|
{
|
||||||
|
private $_redirect_params;
|
||||||
|
|
||||||
|
private $_header_params;
|
||||||
|
|
||||||
|
public function __construct(array $redirect_params)
|
||||||
|
{
|
||||||
|
// vde($redirect_params);
|
||||||
|
# Todo: not sure what will be in second index
|
||||||
|
# for now, only http status.
|
||||||
|
list($this->_redirect_params, $this->_header_params) = $redirect_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _render_view()
|
||||||
|
{
|
||||||
|
$url = \Rails::application()->router()->urlFor($this->_redirect_params);
|
||||||
|
\Rails::application()->dispatcher()->response()->headers()->location($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _print_view()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
85
lib/Rails/ActionController/Response/Template.php
Executable file
85
lib/Rails/ActionController/Response/Template.php
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
class Template extends Base
|
||||||
|
{
|
||||||
|
private $_xml;
|
||||||
|
|
||||||
|
protected $renderer;
|
||||||
|
|
||||||
|
protected $template_file_name;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
|
||||||
|
$this->build_template_file_name();
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'layout' => $this->_params['layout']
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->renderer = new ActionView\Template($this->template_file_name, $params);
|
||||||
|
|
||||||
|
$locals = Rails::application()->controller()->vars();
|
||||||
|
|
||||||
|
if (!empty($this->_params['is_xml'])) {
|
||||||
|
$this->_xml = new ActionView\Xml();
|
||||||
|
$locals->xml = $this->_xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->renderer->setLocals($locals);
|
||||||
|
|
||||||
|
$this->renderer->renderContent();
|
||||||
|
} catch (ActionView\Template\Exception\ExceptionInterface $e) {
|
||||||
|
switch (get_class($e)) {
|
||||||
|
case 'Rails\ActionView\Template\Exception\TemplateMissingException':
|
||||||
|
$route = Rails::application()->router()->route();
|
||||||
|
if (Rails::application()->dispatcher()->controller()->actionRan()) {
|
||||||
|
throw new Exception\ViewNotFoundException(
|
||||||
|
sprintf("View file not found: %s", $this->template_file_name)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if ($route->namespaces())
|
||||||
|
$namespaces = ' [ namespaces => [ ' . implode(', ', $route->namespaces()) . ' ] ]';
|
||||||
|
else
|
||||||
|
$namespaces = '';
|
||||||
|
|
||||||
|
throw new Exception\ActionNotFoundException(
|
||||||
|
// sprintf("Action '%s' not found for controller '%s'%s", $route->action(), $route->controller(), $namespaces)
|
||||||
|
sprintf("Action '%s' not found for controller '%s'%s", $route->action(), $route->controller(), $namespaces)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// default:
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
// if (!empty($this->_params['is_xml']))
|
||||||
|
// return $this->_xml->output();
|
||||||
|
// else
|
||||||
|
return $this->renderer->get_buffer_and_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_template_file_name()
|
||||||
|
{
|
||||||
|
$views_path = Rails::config()->paths->views;
|
||||||
|
|
||||||
|
if (is_array($this->_params['extension']))
|
||||||
|
$ext = implode('.', $this->_params['extension']);
|
||||||
|
else
|
||||||
|
$ext = $this->_params['extension'];
|
||||||
|
|
||||||
|
$this->template_file_name = $views_path . DIRECTORY_SEPARATOR . $this->_params['template_name'] . '.' . $ext;
|
||||||
|
}
|
||||||
|
}
|
64
lib/Rails/ActionController/Response/View.php
Executable file
64
lib/Rails/ActionController/Response/View.php
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionView;
|
||||||
|
|
||||||
|
class View extends Base
|
||||||
|
{
|
||||||
|
private $_xml;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
|
||||||
|
$this->_renderer = new ActionView\Template($this->_params, $this->_params['layout']);
|
||||||
|
|
||||||
|
$locals = Rails::application()->controller()->vars();
|
||||||
|
|
||||||
|
if (!empty($this->_params['is_xml'])) {
|
||||||
|
$this->_xml = new ActionView\Xml();
|
||||||
|
$locals->xml = $this->_xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_renderer->setLocals($locals);
|
||||||
|
|
||||||
|
$this->_renderer->render_content();
|
||||||
|
} catch (ActionView\Template\Exception\ExceptionInterface $e) {
|
||||||
|
switch (get_class($e)) {
|
||||||
|
case 'Rails\ActionView\Template\Exception\LayoutMissingException':
|
||||||
|
case 'Rails\ActionView\Template\Exception\TemplateMissingException':
|
||||||
|
$route = $this->router()->route();
|
||||||
|
if (Rails::application()->dispatcher()->action_ran()) {
|
||||||
|
$token = $route->to();
|
||||||
|
throw new Exception\ViewNotFoundException(
|
||||||
|
sprintf("View for %s not found", $token)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if ($route->namespaces())
|
||||||
|
$namespaces = ' [ namespaces => [ ' . implode(', ', $route->namespaces()) . ' ] ]';
|
||||||
|
else
|
||||||
|
$namespaces = '';
|
||||||
|
|
||||||
|
throw new Exception\ActionNotFoundException(
|
||||||
|
sprintf("Action '%s' not found for controller '%s'%s", $route->action(), $route->controller(), $namespace)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw $e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
if (!empty($this->_params['is_xml']))
|
||||||
|
return $this->_xml->output();
|
||||||
|
else
|
||||||
|
return $this->_renderer->get_buffer_and_clean();
|
||||||
|
}
|
||||||
|
}
|
29
lib/Rails/ActionController/Response/Xml.php
Executable file
29
lib/Rails/ActionController/Response/Xml.php
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionController\Response;
|
||||||
|
|
||||||
|
class Xml extends Base
|
||||||
|
{
|
||||||
|
private $_xml;
|
||||||
|
|
||||||
|
public function _render_view()
|
||||||
|
{
|
||||||
|
$el = array_shift($this->_params);
|
||||||
|
|
||||||
|
if ($el instanceof \Rails\ActiveRecord\Collection) {
|
||||||
|
$this->_xml = new \Rails\ActionView\Xml();
|
||||||
|
$root = $this->_params['root'];
|
||||||
|
$this->_xml->instruct();
|
||||||
|
$this->_xml->$root([], function() use ($el) {
|
||||||
|
foreach ($el as $model) {
|
||||||
|
$model->toXml(['builder' => $this->_xml, 'skip_instruct' => true]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
$this->_xml = new \Rails\Xml\Xml($el, $this->_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _print_view()
|
||||||
|
{
|
||||||
|
return $this->_xml->output();
|
||||||
|
}
|
||||||
|
}
|
143
lib/Rails/ActionDispatch/ActionDispatch.php
Executable file
143
lib/Rails/ActionDispatch/ActionDispatch.php
Executable file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\Response;
|
||||||
|
use Rails\Routing;
|
||||||
|
|
||||||
|
class ActionDispatch
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ActionController_Response instance.
|
||||||
|
*/
|
||||||
|
private $_response;
|
||||||
|
|
||||||
|
private
|
||||||
|
$_parameters,
|
||||||
|
$_request,
|
||||||
|
$_session,
|
||||||
|
$_headers;
|
||||||
|
|
||||||
|
private $_router;
|
||||||
|
|
||||||
|
private $_action_ran = false;
|
||||||
|
|
||||||
|
private $_action_dispatched = false;
|
||||||
|
|
||||||
|
private $_view;
|
||||||
|
|
||||||
|
private $_responded = false;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->_response = new Response();
|
||||||
|
$this->_router = new Routing\Router();
|
||||||
|
$this->_response->_init();
|
||||||
|
$this->_headers = new Http\Headers();
|
||||||
|
$this->load_request_and_params();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load_request_and_params()
|
||||||
|
{
|
||||||
|
if (!$this->_parameters) {
|
||||||
|
$this->_request = new Request();
|
||||||
|
$this->_parameters = new Http\Parameters();
|
||||||
|
$this->_session = new Http\Session();
|
||||||
|
} else {
|
||||||
|
throw new Exception\LogicException("Can't call init() more than once");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find_route()
|
||||||
|
{
|
||||||
|
$this->_router->find_route();
|
||||||
|
$this->_route_vars_to_params();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function router()
|
||||||
|
{
|
||||||
|
return $this->_router;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method shouldn't be accessed like Rails::application()->dispatcher()->parameters();
|
||||||
|
*/
|
||||||
|
public function parameters()
|
||||||
|
{
|
||||||
|
return $this->_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request()
|
||||||
|
{
|
||||||
|
return $this->_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headers()
|
||||||
|
{
|
||||||
|
return $this->_headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function controller()
|
||||||
|
{
|
||||||
|
return Rails::application()->controller();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function session()
|
||||||
|
{
|
||||||
|
return $this->_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function response()
|
||||||
|
{
|
||||||
|
return $this->_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function respond()
|
||||||
|
{
|
||||||
|
// if ($this->_responded && !Rails::response_params()) {
|
||||||
|
if ($this->_responded) {
|
||||||
|
throw new Exception\LogicException("Can't respond to request more than once");
|
||||||
|
} else {
|
||||||
|
$this->_response->_respond();
|
||||||
|
$this->_responded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public function clear_responded()
|
||||||
|
// {
|
||||||
|
// static $cleared = false;
|
||||||
|
|
||||||
|
// if ($cleared) {
|
||||||
|
// throw new Rails_ActionDispatch_Exception("Can't clear response more than once");
|
||||||
|
// } else {
|
||||||
|
// $cleared = true;
|
||||||
|
// $this->_responded = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
private function _route_vars_to_params()
|
||||||
|
{
|
||||||
|
$vars = $this->_router->route()->vars();
|
||||||
|
unset($vars['controller'], $vars['action']);
|
||||||
|
foreach ($vars as $name => $val) {
|
||||||
|
if ($this->_parameters->$name === null)
|
||||||
|
$this->_parameters->$name = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _action_name()
|
||||||
|
{
|
||||||
|
return $this->router()->route()->action;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _action_exists()
|
||||||
|
{
|
||||||
|
$controller = Rails::application()->controller();
|
||||||
|
return method_exists($controller, $this->_action_name()) && is_callable(array($controller, $this->_action_name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _app()
|
||||||
|
{
|
||||||
|
return Rails::application();
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionDispatch/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Exception/LogicException.php
Executable file
6
lib/Rails/ActionDispatch/Exception/LogicException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Exception;
|
||||||
|
|
||||||
|
class LogicException extends \Rails\Exception\LogicException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Exception/RuntimeException.php
Executable file
6
lib/Rails/ActionDispatch/Exception/RuntimeException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Http/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionDispatch/Http/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\Http\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Http/Exception/InvalidArgumentException.php
Executable file
6
lib/Rails/ActionDispatch/Http/Exception/InvalidArgumentException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\Http\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \Rails\Exception\InvalidArgumentException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionDispatch/Http/Exception/LogicException.php
Executable file
6
lib/Rails/ActionDispatch/Http/Exception/LogicException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\Http\Exception;
|
||||||
|
|
||||||
|
class LogicException extends \Rails\Exception\LogicException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
166
lib/Rails/ActionDispatch/Http/Headers.php
Executable file
166
lib/Rails/ActionDispatch/Http/Headers.php
Executable file
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Http;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
class Headers
|
||||||
|
{
|
||||||
|
private $headers = array();
|
||||||
|
|
||||||
|
private $status = 200;
|
||||||
|
|
||||||
|
private $status_sent = false;
|
||||||
|
|
||||||
|
private $_content_type;
|
||||||
|
|
||||||
|
// public function status()
|
||||||
|
// {
|
||||||
|
// return $this->status;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public function status($status = null)
|
||||||
|
{
|
||||||
|
if (null === $status) {
|
||||||
|
return $this->status;
|
||||||
|
} else {
|
||||||
|
if (ctype_digit((string)$status))
|
||||||
|
$this->status = (int)$status;
|
||||||
|
elseif (is_string($status))
|
||||||
|
$this->status = $status;
|
||||||
|
else
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("%s accepts string, %s passed.", __METHOD__, gettype($value))
|
||||||
|
);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function location($url, $status = 302)
|
||||||
|
{
|
||||||
|
if (!is_string($url))
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("%s accepts string as first parameter, %s passed.", __METHOD__, gettype($value))
|
||||||
|
);
|
||||||
|
elseif (!is_int($status) && !is_string($status))
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("%s accepts string or int as second parameter, %s passed.", __METHOD__, gettype($status))
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->status($status)->set('Location', $url);
|
||||||
|
$this->status = $status;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($name, $value = null)
|
||||||
|
{
|
||||||
|
return $this->add($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($name, $value = null)
|
||||||
|
{
|
||||||
|
if (!is_string($name))
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("First argument for %s must be a string, %s passed.", __METHOD__, gettype($value))
|
||||||
|
);
|
||||||
|
elseif (!is_null($value) && !is_string($value) && !is_int($value))
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("%s accepts null, string or int as second argument, %s passed.", __METHOD__, gettype($value))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (strpos($name, 'Content-type') === 0) {
|
||||||
|
if ($value !== null) {
|
||||||
|
$name = $name . $value;
|
||||||
|
}
|
||||||
|
$this->contentType($name);
|
||||||
|
} elseif ($name == 'status') {
|
||||||
|
$this->status($value);
|
||||||
|
} elseif (strpos($name, 'HTTP/') === 0) {
|
||||||
|
$this->status($name);
|
||||||
|
} else {
|
||||||
|
if ($value === null) {
|
||||||
|
if (count(explode(':', $name)) < 2)
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("%s is not a valid header", $name)
|
||||||
|
);
|
||||||
|
$this->headers[] = $name;
|
||||||
|
} else {
|
||||||
|
$this->headers[$name] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
if ($this->status_sent) {
|
||||||
|
throw new Exception\LogicException("Headers have already been sent, can't send them twice");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->_content_type) {
|
||||||
|
$this->_set_default_content_type();
|
||||||
|
}
|
||||||
|
header($this->_content_type);
|
||||||
|
// vpe($this->_content_type);
|
||||||
|
foreach ($this->headers as $name => $value) {
|
||||||
|
if (!is_int($name))
|
||||||
|
$value = $name . ': ' . $value;
|
||||||
|
header($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_int($this->status))
|
||||||
|
header('HTTP/1.1 ' . $this->status);
|
||||||
|
else
|
||||||
|
header($this->status);
|
||||||
|
|
||||||
|
$this->status_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contentType($content_type = null)
|
||||||
|
{
|
||||||
|
if (null === $content_type) {
|
||||||
|
return $this->_content_type;
|
||||||
|
} else {
|
||||||
|
return $this->setContentType($content_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContentType($content_type)
|
||||||
|
{
|
||||||
|
// static $i = 0;
|
||||||
|
|
||||||
|
if (!is_string($content_type)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Content type must be a string, %s passed", gettype($content_type))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if($content_type != "text/html; charset=utf-8")
|
||||||
|
// if ($i)
|
||||||
|
// vpe($content_type);
|
||||||
|
switch ($content_type) {
|
||||||
|
case 'html':
|
||||||
|
$content_type = 'text/html';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'json':
|
||||||
|
$content_type = 'application/json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'xml':
|
||||||
|
$content_type = 'application/xml';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($content_type, 'Content-type:') !== 0)
|
||||||
|
$content_type = 'Content-type: ' . $content_type;
|
||||||
|
|
||||||
|
$this->_content_type = $content_type;
|
||||||
|
// $i++;
|
||||||
|
// vpe($content_type);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _set_default_content_type()
|
||||||
|
{
|
||||||
|
// $this->set_content_type('text/html; charset='.Rails::application()->config()->encoding);
|
||||||
|
}
|
||||||
|
}
|
314
lib/Rails/ActionDispatch/Http/Parameters.php
Executable file
314
lib/Rails/ActionDispatch/Http/Parameters.php
Executable file
@ -0,0 +1,314 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Http;
|
||||||
|
|
||||||
|
use Rails\Toolbox\ArrayTools;
|
||||||
|
use Rails\ArrayHelper\GlobalVar;
|
||||||
|
|
||||||
|
class Parameters implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
private
|
||||||
|
$deleteVars = [],
|
||||||
|
$putVars = [],
|
||||||
|
$_json_params_error = null,
|
||||||
|
$patchVars = [],
|
||||||
|
# Parameters for request methods other than
|
||||||
|
# delete, put, post, get, patchVars (need to support head requests).
|
||||||
|
$other_params = [];
|
||||||
|
|
||||||
|
private $files;
|
||||||
|
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
return new ArrayIterator($this->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$method = \Rails::application()->dispatcher()->request()->method();
|
||||||
|
if ($method != 'GET' && $method != 'POST') {
|
||||||
|
$params = file_get_contents('php://input');
|
||||||
|
$decoded = [];
|
||||||
|
if (!empty($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] == "application/json") {
|
||||||
|
$decoded = json_decode($params, true);
|
||||||
|
if ($decoded === null) {
|
||||||
|
$decoded = [];
|
||||||
|
$this->_json_params_error = json_last_error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parse_str($params, $decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method == 'DELETE')
|
||||||
|
$this->deleteVars = $decoded;
|
||||||
|
elseif ($method == 'PUT')
|
||||||
|
$this->putVars = $decoded;
|
||||||
|
elseif ($method == 'PATCH')
|
||||||
|
$this->patchVars = $decoded;
|
||||||
|
else
|
||||||
|
$this->other_params = $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_import_files();
|
||||||
|
// vpe($this->files);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $value)
|
||||||
|
{
|
||||||
|
if ($var = $this->_search($prop))
|
||||||
|
global ${$var};
|
||||||
|
|
||||||
|
if (is_object($value)) {
|
||||||
|
if ($var)
|
||||||
|
$this->$prop = ${$var}[$prop];
|
||||||
|
else
|
||||||
|
$this->$prop = $value;
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
if ($var)
|
||||||
|
$value = new GlobalVar($value, $var, $prop);
|
||||||
|
$this->$prop = $value;
|
||||||
|
} else {
|
||||||
|
if ($var)
|
||||||
|
${$var}[$prop] = $value;
|
||||||
|
else
|
||||||
|
$this->$prop = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($prop)
|
||||||
|
{
|
||||||
|
$ret = null;
|
||||||
|
$var = $this->_search($prop);
|
||||||
|
if ($var) {
|
||||||
|
global ${$var};
|
||||||
|
|
||||||
|
if (is_array(${$var}[$prop])) {
|
||||||
|
// if (isset($this->files[$prop])) {
|
||||||
|
// ${$var}[$prop] = array_merge(${$var}[$prop], $this->files[$prop]);
|
||||||
|
// }
|
||||||
|
$this->$prop = new GlobalVar(${$var}[$prop], $var, $prop);
|
||||||
|
# Return here.
|
||||||
|
return $this->$prop;
|
||||||
|
} elseif (is_object(${$var}[$prop])) {
|
||||||
|
$this->$prop = ${$var}[$prop];
|
||||||
|
$ret = $this->$prop;
|
||||||
|
} else {
|
||||||
|
$ret = ${$var}[$prop];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset($this->putVars[$prop]))
|
||||||
|
$ret = $this->putVars[$prop];
|
||||||
|
elseif (isset($this->deleteVars[$prop]))
|
||||||
|
$ret = $this->deleteVars[$prop];
|
||||||
|
elseif (isset($this->patchVars[$prop])) {
|
||||||
|
$ret = $this->patchVars[$prop];
|
||||||
|
// elseif (isset($this->files[$prop])) {
|
||||||
|
# Return here.
|
||||||
|
// return $this->files[$prop];
|
||||||
|
} elseif (isset($this->other_params[$prop]))
|
||||||
|
$ret = $this->other_params[$prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ($ret && $this->files) {
|
||||||
|
// vpe($this->files);
|
||||||
|
// $this->mergeWithFiles($ret, $prop);
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($prop)
|
||||||
|
{
|
||||||
|
return $this->_search($prop) || isset($this->deleteVars[$prop]) || isset($this->putVars[$prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function del($prop)
|
||||||
|
{
|
||||||
|
unset($this->$prop, $_GET[$prop], $_POST[$prop], $this->deleteVars[$prop], $this->putVars[$prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $_GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post()
|
||||||
|
{
|
||||||
|
return $_POST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
return $this->deleteVars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function put()
|
||||||
|
{
|
||||||
|
return $this->putVars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch()
|
||||||
|
{
|
||||||
|
return $this->patchVars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function files()
|
||||||
|
{
|
||||||
|
return $this->files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
get_object_vars($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$obj_vars = get_object_vars($this);
|
||||||
|
unset($obj_vars['deleteVars'], $obj_vars['putVars'], $obj_vars['_json_params_error'], $obj_vars['patchVars'], $obj_vars['other_params'], $obj_vars['files']);
|
||||||
|
|
||||||
|
$ret = array_merge_recursive($_POST, $_GET, $obj_vars, $this->deleteVars, $this->putVars, $this->patchVars, $this->other_params/*, $this->files*/);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function all()
|
||||||
|
{
|
||||||
|
return $this->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function merge()
|
||||||
|
{
|
||||||
|
$params = func_get_args();
|
||||||
|
array_unshift($params, $this->all());
|
||||||
|
return call_user_func_array('array_merge', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function json_params_error()
|
||||||
|
{
|
||||||
|
return $this->_json_params_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _search($prop)
|
||||||
|
{
|
||||||
|
if (isset($_GET[$prop]))
|
||||||
|
return '_GET';
|
||||||
|
elseif (isset($_POST[$prop]))
|
||||||
|
return '_POST';
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mergeWithFiles(&$array, $prop)
|
||||||
|
{
|
||||||
|
if (isset($this->files->$prop)) {
|
||||||
|
foreach ($this->files->$prop as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
if (!isset($array[$key])) {
|
||||||
|
$array[$key] = [];
|
||||||
|
} elseif (!is_array($array[$key])) {
|
||||||
|
$array[$key] = [ $array[$key] ];
|
||||||
|
}
|
||||||
|
$array[$key] = array_merge($array[$key], $value);
|
||||||
|
} else {
|
||||||
|
$array[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _import_files()
|
||||||
|
{
|
||||||
|
if (empty($_FILES)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->files = new \stdClass();
|
||||||
|
|
||||||
|
foreach ($_FILES as $mainName => $data) {
|
||||||
|
if (!is_array($data['name']) && $data['error'] != UPLOAD_ERR_NO_FILE) {
|
||||||
|
$this->files->$mainName = new UploadedFile($_FILES[$mainName]);
|
||||||
|
} else {
|
||||||
|
$this->files->$mainName = $this->_get_subnames($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _get_subnames(array $arr)
|
||||||
|
{
|
||||||
|
$arranged = new \ArrayObject();
|
||||||
|
// $arranged = [];
|
||||||
|
|
||||||
|
foreach ($arr['name'] as $k => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
if ($arr['error'] != UPLOAD_ERR_NO_FILE) {
|
||||||
|
$arranged[$k] = [
|
||||||
|
'name' => $value,
|
||||||
|
'type' => $arr['type'][$k],
|
||||||
|
'tmp_name' => $arr['tmp_name'][$k],
|
||||||
|
'error' => $arr['error'][$k],
|
||||||
|
'size' => $arr['size'][$k],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$keys = ['name', $k];
|
||||||
|
$this->_get_subnames_2($arranged, $keys, $arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $arranged->getArrayCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _get_subnames_2($arranged, $keys, $arr)
|
||||||
|
{
|
||||||
|
$baseArr = $arr;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$baseArr = $baseArr[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($baseArr as $k => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$this->setArranged($arranged, array_merge($keys, [$k]), [
|
||||||
|
'name' => $value,
|
||||||
|
'type' => $this->foreachKeys(array_merge(['type'] + $keys, [$k]), $arr),
|
||||||
|
'tmp_name' => $this->foreachKeys(array_merge(['tmp_name'] + $keys, [$k]), $arr),
|
||||||
|
'error' => $this->foreachKeys(array_merge(['error'] + $keys, [$k]), $arr),
|
||||||
|
'size' => $this->foreachKeys(array_merge(['size'] + $keys, [$k]), $arr),
|
||||||
|
]);
|
||||||
|
// vpe($arranged, $key, $k);
|
||||||
|
// $arranged[$k] = $arranged[$k]->getArrayCopy();
|
||||||
|
} else {
|
||||||
|
$tmpKeys = $keys;
|
||||||
|
$tmpKeys[] = $k;
|
||||||
|
$this->_get_subnames_2($arranged, $tmpKeys, $arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function foreachKeys($keys, $arr)
|
||||||
|
{
|
||||||
|
$baseArr = $arr;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$baseArr = $baseArr[$key];
|
||||||
|
}
|
||||||
|
return $baseArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setArranged($arr, $keys, $val)
|
||||||
|
{
|
||||||
|
if ($val['error'] == UPLOAD_ERR_NO_FILE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_shift($keys);
|
||||||
|
$lastKey = array_pop($keys);
|
||||||
|
$baseArr = &$arr;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!isset($baseArr[$key])) {
|
||||||
|
// $baseArr[$key] = new \ArrayObject();
|
||||||
|
$baseArr[$key] = [];
|
||||||
|
}
|
||||||
|
$baseArr = &$baseArr[$key];
|
||||||
|
}
|
||||||
|
$baseArr[$lastKey] = new UploadedFile($val);
|
||||||
|
}
|
||||||
|
}
|
64
lib/Rails/ActionDispatch/Http/Session.php
Executable file
64
lib/Rails/ActionDispatch/Http/Session.php
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Http;
|
||||||
|
|
||||||
|
use Rails\ArrayHelper\GlobalVar;
|
||||||
|
|
||||||
|
class Session implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
return new \ArrayIterator($_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $value)
|
||||||
|
{
|
||||||
|
$this->set($prop, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($prop)
|
||||||
|
{
|
||||||
|
return $this->get($prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($prop, $value)
|
||||||
|
{
|
||||||
|
if (is_object($value)) {
|
||||||
|
$this->$prop = $value;
|
||||||
|
$_SESSION[$prop] = $value;
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$arr = new GlobalVar($value, '_SESSION', $prop);
|
||||||
|
$this->$prop = $arr;
|
||||||
|
} else {
|
||||||
|
$_SESSION[$prop] = $value;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($prop)
|
||||||
|
{
|
||||||
|
if (isset($_SESSION[$prop])) {
|
||||||
|
if (is_array($_SESSION[$prop])) {
|
||||||
|
$this->$prop = new GlobalVar($_SESSION[$prop], '_SESSION', $prop);
|
||||||
|
return $this->$prop;
|
||||||
|
} elseif (is_object($_SESSION[$prop])) {
|
||||||
|
$this->$prop = $_SESSION[$prop];
|
||||||
|
return $this->$prop;
|
||||||
|
} else {
|
||||||
|
return $_SESSION[$prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($prop)
|
||||||
|
{
|
||||||
|
unset($this->$prop, $_SESSION[$prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function merge()
|
||||||
|
{
|
||||||
|
$params = func_get_args();
|
||||||
|
array_unshift($params, $_SESSION);
|
||||||
|
return call_user_func_array('array_merge', $params);
|
||||||
|
}
|
||||||
|
}
|
59
lib/Rails/ActionDispatch/Http/UploadedFile.php
Executable file
59
lib/Rails/ActionDispatch/Http/UploadedFile.php
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch\Http;
|
||||||
|
|
||||||
|
class UploadedFile
|
||||||
|
{
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
protected $tempName;
|
||||||
|
|
||||||
|
protected $error;
|
||||||
|
|
||||||
|
protected $size;
|
||||||
|
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->name = $data['name'];
|
||||||
|
$this->type = $data['type'];
|
||||||
|
$this->tempName = $data['tmp_name'];
|
||||||
|
$this->error = $data['error'];
|
||||||
|
$this->size = $data['size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function type()
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tempName()
|
||||||
|
{
|
||||||
|
return $this->tempName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function size()
|
||||||
|
{
|
||||||
|
return $this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorCode()
|
||||||
|
{
|
||||||
|
return $this->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error()
|
||||||
|
{
|
||||||
|
return !($this->error == UPLOAD_ERR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function move($newName)
|
||||||
|
{
|
||||||
|
return move_uploaded_file($this->tempName, $newName);
|
||||||
|
}
|
||||||
|
}
|
175
lib/Rails/ActionDispatch/Request.php
Executable file
175
lib/Rails/ActionDispatch/Request.php
Executable file
@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionDispatch;
|
||||||
|
|
||||||
|
class Request
|
||||||
|
{
|
||||||
|
const LOCALHOST = '127.0.0.1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of methods allowed through the _method parameter
|
||||||
|
* in a POST request.
|
||||||
|
*/
|
||||||
|
static private $allowedHackMethods = [
|
||||||
|
'PUT',
|
||||||
|
'PATCH',
|
||||||
|
'DELETE',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request path without the query string.
|
||||||
|
* The application's basePath (i.e. if the app is ran under a subdirectory),
|
||||||
|
* is cut off.
|
||||||
|
* To get the complete path, call originalPath.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function path()
|
||||||
|
{
|
||||||
|
return substr($this->originalPath(), strlen(\Rails::application()->router()->basePath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request path without the query string.
|
||||||
|
* The application's basePath is included.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function originalPath()
|
||||||
|
{
|
||||||
|
if (is_int($pos = strpos($this->get('REQUEST_URI'), '?'))) {
|
||||||
|
return substr($this->get('REQUEST_URI'), 0, $pos);
|
||||||
|
}
|
||||||
|
return substr($this->get('REQUEST_URI'), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full request path, includes query string, but excludes basePath.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fullPath()
|
||||||
|
{
|
||||||
|
return substr($this->get('REQUEST_URI'), strlen(\Rails::application()->router()->basePath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full request path, includes both basePath and query string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function originalFullPath()
|
||||||
|
{
|
||||||
|
return $this->get('REQUEST_URI');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function controller()
|
||||||
|
{
|
||||||
|
if (!($router = \Rails::application()->dispatcher()->router()) || !($route = $router->route()))
|
||||||
|
return false;
|
||||||
|
return $route->controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action()
|
||||||
|
{
|
||||||
|
if (!($router = \Rails::application()->dispatcher()->router()) || !($route = $router->route()))
|
||||||
|
return false;
|
||||||
|
return $route->action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isGet()
|
||||||
|
{
|
||||||
|
return $this->method() === 'GET';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPost()
|
||||||
|
{
|
||||||
|
return $this->method() == 'POST';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPut()
|
||||||
|
{
|
||||||
|
return $this->method() == 'PUT';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDelete()
|
||||||
|
{
|
||||||
|
return $this->method() == 'DELETE';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPatch()
|
||||||
|
{
|
||||||
|
return $this->method() == 'PATCH';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the request method.
|
||||||
|
*/
|
||||||
|
public function is($method)
|
||||||
|
{
|
||||||
|
$method = strtoupper($method);
|
||||||
|
return $this->method() == $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLocal()
|
||||||
|
{
|
||||||
|
return \Rails::config()->consider_all_requests_local ?: $this->remoteIp() == self::LOCALHOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remoteIp()
|
||||||
|
{
|
||||||
|
if ($this->get('HTTP_CLIENT_IP'))
|
||||||
|
$remoteIp = $this->get('HTTP_CLIENT_IP');
|
||||||
|
elseif ($this->get('HTTP_X_FORWARDED_FOR'))
|
||||||
|
$remoteIp = $this->get('HTTP_X_FORWARDED_FOR');
|
||||||
|
else
|
||||||
|
$remoteIp = $this->get('REMOTE_ADDR');
|
||||||
|
return $remoteIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the overridden method name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function method()
|
||||||
|
{
|
||||||
|
if (isset($_POST['_method'])) {
|
||||||
|
$method = strtoupper($_POST['_method']);
|
||||||
|
if (in_array($method, self::$allowedHackMethods)) {
|
||||||
|
return $method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->get('REQUEST_METHOD');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function protocol()
|
||||||
|
{
|
||||||
|
$protocol = ($val = $this->get('HTTPS')) && $val !== 'off' ? 'https' : 'http';
|
||||||
|
return $protocol . '://';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isXmlHttpRequest()
|
||||||
|
{
|
||||||
|
return (($var = $this->get("HTTP_X_REQUESTED_WITH"))) && $var === "XMLHttpRequest";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function format()
|
||||||
|
{
|
||||||
|
if ($route = \Rails::application()->dispatcher()->router()->route())
|
||||||
|
return $route->format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an index in the $_SERVER superglobal.
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function get($name)
|
||||||
|
{
|
||||||
|
$name = strtoupper($name);
|
||||||
|
if (isset($_SERVER[$name])) {
|
||||||
|
return $_SERVER[$name];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
147
lib/Rails/ActionMailer/ActionMailer.php
Executable file
147
lib/Rails/ActionMailer/ActionMailer.php
Executable file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer;
|
||||||
|
|
||||||
|
use Zend\Mail;
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
abstract class ActionMailer
|
||||||
|
{
|
||||||
|
static protected $transport;
|
||||||
|
|
||||||
|
static public function load_mailer($name, $raise_exception = true)
|
||||||
|
{
|
||||||
|
if (!class_exists($name, false)) {
|
||||||
|
$mails_path = Rails::config()->paths->mailers;
|
||||||
|
$file = $mails_path . '/' . Rails::services()->get('inflector')->underscore($name) . '.php';
|
||||||
|
if (!is_file($file)) {
|
||||||
|
if ($raise_exception)
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf('No file found for mailer %s (searched in %s)', $name, $file)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
if (!class_exists($name, false)) {
|
||||||
|
if ($raise_exception)
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("File for mailer %s doesn't contain expected class", $name)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::init();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function transport(Mail\Transport\TransportInterface $transport = null)
|
||||||
|
{
|
||||||
|
if (null !== $transport) {
|
||||||
|
self::$transport = $transport;
|
||||||
|
} elseif (!self::$transport) {
|
||||||
|
self::setDefaultTransport();
|
||||||
|
}
|
||||||
|
return self::$transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function filenameGenerator()
|
||||||
|
{
|
||||||
|
return 'action_mailer_' . $_SERVER['REQUEST_TIME'] . '_' . mt_rand() . '.tmp';
|
||||||
|
}
|
||||||
|
|
||||||
|
static protected function setDefaultTransport()
|
||||||
|
{
|
||||||
|
$config = Rails::application()->config()->action_mailer;
|
||||||
|
|
||||||
|
switch ($config['delivery_method']) {
|
||||||
|
/**
|
||||||
|
* Rails to Zend options:
|
||||||
|
* address => name
|
||||||
|
* domain => host
|
||||||
|
* port => port
|
||||||
|
* authentication => connection_class
|
||||||
|
* user_name => connection_config[username]
|
||||||
|
* password => connection_config[password]
|
||||||
|
* enable_starttls_auto (true) => connection_config[ssl] => 'tls'
|
||||||
|
* enable_starttls_auto (false) => connection_config[ssl] => 'ssl'
|
||||||
|
*/
|
||||||
|
case 'smtp':
|
||||||
|
$defaultConfig = [
|
||||||
|
'address' => '127.0.0.1',
|
||||||
|
'domain' => 'localhost',
|
||||||
|
'port' => 25,
|
||||||
|
'user_name' => '',
|
||||||
|
'password' => '',
|
||||||
|
|
||||||
|
'enable_starttls_auto' => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Differences with RoR:
|
||||||
|
* - ZF2 adds the "smtp" option
|
||||||
|
* - The "cram_md5" option is called "crammd5"
|
||||||
|
*/
|
||||||
|
'authentication' => 'login'
|
||||||
|
];
|
||||||
|
|
||||||
|
$smtp = array_merge($defaultConfig, $config['smtp_settings']->toArray());
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
'host' => $smtp['address'],
|
||||||
|
'name' => $smtp['domain'],
|
||||||
|
'port' => $smtp['port'],
|
||||||
|
'connection_class' => $smtp['authentication'],
|
||||||
|
'connection_config' => [
|
||||||
|
'username' => $smtp['user_name'],
|
||||||
|
'password' => $smtp['password'],
|
||||||
|
'ssl' => $smtp['enable_starttls_auto'] ? 'tls' : null,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$options = new Mail\Transport\SmtpOptions($options);
|
||||||
|
|
||||||
|
$transport = new Mail\Transport\Smtp();
|
||||||
|
|
||||||
|
$transport->setOptions($options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* location => path
|
||||||
|
* name_generator => callback
|
||||||
|
*/
|
||||||
|
case 'file':
|
||||||
|
$customOpts = $config['file_settings'];
|
||||||
|
$options = [];
|
||||||
|
|
||||||
|
if ($customOpts['location'] === null) {
|
||||||
|
$dir = Rails::root() . '/tmp/mail';
|
||||||
|
if (!is_dir($dir))
|
||||||
|
mkdir($dir, 0777, true);
|
||||||
|
$customOpts['location'] = $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
$options['path'] = $customOpts['location'];
|
||||||
|
|
||||||
|
if ($customOpts['name_generator'] === null) {
|
||||||
|
$options['callback'] = 'Rails\ActionMailer\ActionMailer::filenameGenerator';
|
||||||
|
} else {
|
||||||
|
$options['callback'] = $customOpts['name_generator'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileOptions = new Mail\Transport\FileOptions($options);
|
||||||
|
|
||||||
|
$transport = new Mail\Transport\File();
|
||||||
|
|
||||||
|
$transport->setOptions($fileOptions);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($config['delivery_method'] instanceof Closure):
|
||||||
|
$transport = $config['delivery_method']();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::transport($transport);
|
||||||
|
}
|
||||||
|
}
|
135
lib/Rails/ActionMailer/Base.php
Executable file
135
lib/Rails/ActionMailer/Base.php
Executable file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use Zend\Mime;
|
||||||
|
use Rails;
|
||||||
|
use Rails\Mail\Mail;
|
||||||
|
|
||||||
|
abstract class Base
|
||||||
|
{
|
||||||
|
public $from;
|
||||||
|
|
||||||
|
public $to;
|
||||||
|
|
||||||
|
public $subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: this isn't used anywhere.
|
||||||
|
*/
|
||||||
|
public $partsOrder = ['text/plain', 'text/html'];
|
||||||
|
|
||||||
|
public $templatePath;
|
||||||
|
|
||||||
|
public $charset;
|
||||||
|
|
||||||
|
public $textCharset;
|
||||||
|
|
||||||
|
public $htmlCharset;
|
||||||
|
|
||||||
|
public $templateName;
|
||||||
|
|
||||||
|
public $attachments = [];
|
||||||
|
|
||||||
|
public $calledMethod;
|
||||||
|
|
||||||
|
public $headers = [];
|
||||||
|
|
||||||
|
protected $vars;
|
||||||
|
|
||||||
|
static public function mail($method, array $params = [], array $headers = [])
|
||||||
|
{
|
||||||
|
$cn = get_called_class();
|
||||||
|
$mailer = new $cn();
|
||||||
|
$mailer->calledMethod = $method;
|
||||||
|
$mailer->headers = array_merge($mailer->headers, $headers);
|
||||||
|
if (false !== call_user_func_array([$mailer, $method], $params))
|
||||||
|
return $mailer->createMail();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->vars = new stdClass();
|
||||||
|
$this->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $value)
|
||||||
|
{
|
||||||
|
$this->vars->$prop = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($prop)
|
||||||
|
{
|
||||||
|
if (!isset($this->vars->$prop))
|
||||||
|
return null;
|
||||||
|
return $this->vars->$prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just a quicker way to add an attachment.
|
||||||
|
*/
|
||||||
|
public function attachment($name, $content)
|
||||||
|
{
|
||||||
|
if (!is_string($content) && (!is_resource($content) || get_resource_type($content) != 'stream')) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Attachment content must be either string or stream, %s passed")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attachments[$name] = [
|
||||||
|
'content' => $content
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just a quicker way to add an inline attachment.
|
||||||
|
*/
|
||||||
|
public function inlineAttachment($name, $content)
|
||||||
|
{
|
||||||
|
if (!is_string($content) && (!is_resource($content) || get_resource_type($content) != 'stream')) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Attachment content must be either string or stream, %s passed")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attachments[$name] = [
|
||||||
|
'content' => $content,
|
||||||
|
'inline' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function vars()
|
||||||
|
{
|
||||||
|
return $this->vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for properties can be set in this method.
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createMail()
|
||||||
|
{
|
||||||
|
if (Rails::config()->action_mailer->defaults) {
|
||||||
|
if (!Rails::config()->action_mailer->defaults instanceof \Closure) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
'Configuration action_mailer.defaults must be a closure'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$closure = clone Rails::config()->action_mailer->defaults;
|
||||||
|
$closure = $closure->bindTo($this);
|
||||||
|
$closure();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->templateName)
|
||||||
|
$this->templateName = $this->calledMethod;
|
||||||
|
|
||||||
|
if (!$this->templatePath)
|
||||||
|
$this->templatePath = Rails::services()->get('inflector')->underscore(get_called_class());
|
||||||
|
|
||||||
|
$deliverer = new Deliverer($this);
|
||||||
|
return $deliverer;
|
||||||
|
}
|
||||||
|
}
|
230
lib/Rails/ActionMailer/Deliverer.php
Executable file
230
lib/Rails/ActionMailer/Deliverer.php
Executable file
@ -0,0 +1,230 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionView;
|
||||||
|
use Zend\Mail;
|
||||||
|
use Zend\Mime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and delivers mail.
|
||||||
|
*
|
||||||
|
* This class should only be used by Rails\ActionMailer\Base.
|
||||||
|
* In order to create a custom Mail, Zend\Mail should be used
|
||||||
|
* directly instead.
|
||||||
|
*/
|
||||||
|
class Deliverer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Rails mail that will be processed.
|
||||||
|
*
|
||||||
|
* @var Rails\ActionMailer\Base
|
||||||
|
*/
|
||||||
|
protected $mail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mail message that will be delivered.
|
||||||
|
*
|
||||||
|
* @var Zend\Mail\Message
|
||||||
|
*/
|
||||||
|
protected $message;
|
||||||
|
|
||||||
|
protected $textTemplate;
|
||||||
|
|
||||||
|
protected $htmlTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Zend\Mime\Message
|
||||||
|
*/
|
||||||
|
protected $body;
|
||||||
|
|
||||||
|
public function __construct(Base $mail)
|
||||||
|
{
|
||||||
|
$this->mail = $mail;
|
||||||
|
$this->buildMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deliver()
|
||||||
|
{
|
||||||
|
ActionMailer::transport()->send($this->message);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mail()
|
||||||
|
{
|
||||||
|
return $this->mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildMessage()
|
||||||
|
{
|
||||||
|
$this->message = new Mail\Message();
|
||||||
|
|
||||||
|
$this->setCharset();
|
||||||
|
$this->setFrom();
|
||||||
|
$this->setTo();
|
||||||
|
$this->setSubject();
|
||||||
|
|
||||||
|
ActionView\ViewHelpers::load();
|
||||||
|
|
||||||
|
$this->createTextPart();
|
||||||
|
$this->createHtmlPart();
|
||||||
|
|
||||||
|
$this->body = new Mime\Message();
|
||||||
|
|
||||||
|
$this->addTemplates();
|
||||||
|
$this->addAttachments();
|
||||||
|
|
||||||
|
$this->message->setBody($this->body);
|
||||||
|
|
||||||
|
unset($this->textTemplate, $this->htmlTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCharset()
|
||||||
|
{
|
||||||
|
if (!$charset = $this->mail->charset) {
|
||||||
|
$charset = mb_detect_encoding($this->mail->subject);
|
||||||
|
if (!$charset)
|
||||||
|
$charset = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->message->setEncoding($charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setFrom()
|
||||||
|
{
|
||||||
|
if (!is_array($this->mail->from)) {
|
||||||
|
$email = $this->mail->from;
|
||||||
|
$name = null;
|
||||||
|
} else {
|
||||||
|
list($email, $name) = $this->mail->from;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->message->setFrom($email, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setTo()
|
||||||
|
{
|
||||||
|
$this->message->addTo($this->mail->to);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setSubject()
|
||||||
|
{
|
||||||
|
$this->message->setSubject($this->mail->subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTextPart()
|
||||||
|
{
|
||||||
|
$template_file = $this->templateBasename() . '.text.php';
|
||||||
|
try {
|
||||||
|
$template = new Template($template_file);
|
||||||
|
$template->setLocals($this->mail->vars());
|
||||||
|
$this->textTemplate = $template;
|
||||||
|
} catch (Exception\ExceptionInterface $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createHtmlPart()
|
||||||
|
{
|
||||||
|
$template_file = $this->templateBasename() . '.php';
|
||||||
|
try {
|
||||||
|
$template = new Template($template_file);
|
||||||
|
$template->setLocals($this->mail->vars());
|
||||||
|
$this->htmlTemplate = $template;
|
||||||
|
} catch (Exception\ExceptionInterface $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function templateBasename()
|
||||||
|
{
|
||||||
|
return Rails::config()->paths->views . '/' .
|
||||||
|
$this->mail->templatePath . '/' .
|
||||||
|
$this->mail->templateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addTemplates()
|
||||||
|
{
|
||||||
|
if ($this->textTemplate) {
|
||||||
|
$content = $this->textTemplate->renderContent();
|
||||||
|
$part = new Mime\Part($content);
|
||||||
|
$part->type = 'text/plain';
|
||||||
|
$part->encoding = Mime\Mime::ENCODING_QUOTEDPRINTABLE;
|
||||||
|
$this->body->addPart($part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->htmlTemplate) {
|
||||||
|
$content = $this->htmlTemplate->renderContent();
|
||||||
|
$part = new Mime\Part($content);
|
||||||
|
$part->type = 'text/html';
|
||||||
|
$part->encoding = Mime\Mime::ENCODING_QUOTEDPRINTABLE;
|
||||||
|
$this->body->addPart($part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires Fileinfo.
|
||||||
|
*/
|
||||||
|
private function addAttachments()
|
||||||
|
{
|
||||||
|
if (class_exists('Finfo', false)) {
|
||||||
|
$finfo = new \Finfo(FILEINFO_MIME_TYPE);
|
||||||
|
} else {
|
||||||
|
$finfo = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->mail->attachments as $filename => $attachment) {
|
||||||
|
if (!is_array($attachment)) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Attachments must be array, %s passed", gettype($attachment))
|
||||||
|
);
|
||||||
|
} elseif (
|
||||||
|
!is_string($attachment['content']) &&
|
||||||
|
(
|
||||||
|
!is_resource($attachment['content']) ||
|
||||||
|
!get_resource_type($attachment['content']) == 'stream'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf(
|
||||||
|
"Attachment content must be string or stream, %s passed",
|
||||||
|
gettype($attachment['content'])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = null;
|
||||||
|
|
||||||
|
if (empty($attachment['mime_type']) && $finfo) {
|
||||||
|
if (is_resource($attachment['content'])) {
|
||||||
|
$type = $finfo->buffer(stream_get_contents($attachment['content']));
|
||||||
|
rewind($attachment['content']);
|
||||||
|
} else {
|
||||||
|
$type = $finfo->buffer($attachment['content']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$part = new Mime\Part($attachment['content']);
|
||||||
|
|
||||||
|
if (empty($attachment['encoding'])) {
|
||||||
|
$attachment['encoding'] = Mime\Mime::ENCODING_BASE64;
|
||||||
|
}
|
||||||
|
|
||||||
|
$part->encoding = $attachment['encoding'];
|
||||||
|
|
||||||
|
if ($type) {
|
||||||
|
$part->type = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$part->disposition = !empty($attachment['inline']) ?
|
||||||
|
Mime\Mime::DISPOSITION_INLINE :
|
||||||
|
Mime\Mime::DISPOSITION_ATTACHMENT;
|
||||||
|
|
||||||
|
$this->body->addPart($part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActionMailer/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionMailer/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionMailer/Exception/InvalidArgumentException.php
Executable file
6
lib/Rails/ActionMailer/Exception/InvalidArgumentException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \Rails\Exception\InvalidArgumentException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionMailer/Exception/RuntimeException.php
Executable file
6
lib/Rails/ActionMailer/Exception/RuntimeException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
32
lib/Rails/ActionMailer/Template.php
Executable file
32
lib/Rails/ActionMailer/Template.php
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionMailer;
|
||||||
|
|
||||||
|
class Template extends \Rails\ActionView\Base
|
||||||
|
{
|
||||||
|
private
|
||||||
|
$_template_file,
|
||||||
|
$_contents;
|
||||||
|
|
||||||
|
public function __construct($template_file)
|
||||||
|
{
|
||||||
|
if (!is_file($template_file)) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Template file %s doesn't exist", $template_file)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->_template_file = $template_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderContent()
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
require $this->_template_file;
|
||||||
|
$this->_contents = ob_get_clean();
|
||||||
|
return $this->_contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contents()
|
||||||
|
{
|
||||||
|
return $this->_contents;
|
||||||
|
}
|
||||||
|
}
|
88
lib/Rails/ActionView/ActionView.php
Executable file
88
lib/Rails/ActionView/ActionView.php
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use ReflectionClass;
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
abstract class ActionView
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Stores the contents for.
|
||||||
|
* Static because contents_for are available
|
||||||
|
* for everything.
|
||||||
|
*/
|
||||||
|
private static $_content_for = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the names of the active contentFor's.
|
||||||
|
*/
|
||||||
|
private static $_content_for_names = array();
|
||||||
|
|
||||||
|
protected $_buffer;
|
||||||
|
|
||||||
|
static public function clean_buffers()
|
||||||
|
{
|
||||||
|
if ($status = ob_get_status()) {
|
||||||
|
foreach (range(0, $status['level']) as $lvl)
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates content for $name by calling $block().
|
||||||
|
* If no $block is passed, it's checked if content for $name
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
|
public function contentFor($name, Closure $block = null, $prefix = false)
|
||||||
|
{
|
||||||
|
if (!$block) {
|
||||||
|
return isset(self::$_content_for[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset(self::$_content_for[$name]))
|
||||||
|
self::$_content_for[$name] = '';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$block();
|
||||||
|
$this->_add_content_for($name, ob_get_clean(), $prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provide($name, $content)
|
||||||
|
{
|
||||||
|
$this->_add_content_for($name, $content, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clear_content_for($name)
|
||||||
|
{
|
||||||
|
unset(self::$_content_for[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yield seems to be a reserved keyword in PHP 5.5.
|
||||||
|
* Forced to change the name of the yield method to "content".
|
||||||
|
*/
|
||||||
|
public function content($name = null)
|
||||||
|
{
|
||||||
|
if ($name && isset(self::$_content_for[$name]))
|
||||||
|
return self::$_content_for[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passing a closure to do_content_for() will cause it
|
||||||
|
* to do the same as end_content_for(): add the buffered
|
||||||
|
* content. Thus, this method.
|
||||||
|
*
|
||||||
|
* @param string $name content's name
|
||||||
|
* @param string $value content's body
|
||||||
|
* @param bool $prefix to prefix or not the value to the current value
|
||||||
|
*/
|
||||||
|
private function _add_content_for($name, $value, $prefix)
|
||||||
|
{
|
||||||
|
!array_key_exists($name, self::$_content_for) && self::$_content_for[$name] = '';
|
||||||
|
if ($prefix)
|
||||||
|
self::$_content_for[$name] = $value . self::$_content_for[$name];
|
||||||
|
else
|
||||||
|
self::$_content_for[$name] .= $value;
|
||||||
|
}
|
||||||
|
}
|
204
lib/Rails/ActionView/Base.php
Executable file
204
lib/Rails/ActionView/Base.php
Executable file
@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use Rails;
|
||||||
|
use Rails\Routing\Traits\NamedPathAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for layouts, templates and partials.
|
||||||
|
*/
|
||||||
|
abstract class Base extends ActionView
|
||||||
|
{
|
||||||
|
use NamedPathAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local variables passed.
|
||||||
|
* This could be either an array (partials) or an stdClass
|
||||||
|
* (layouts and templates). They're accessed through __get();
|
||||||
|
*/
|
||||||
|
protected $locals = [];
|
||||||
|
|
||||||
|
public function __get($prop)
|
||||||
|
{
|
||||||
|
return $this->getLocal($prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($prop, $val)
|
||||||
|
{
|
||||||
|
if (!$this->locals) {
|
||||||
|
$this->locals = new stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLocal($prop, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $params)
|
||||||
|
{
|
||||||
|
if ($helper = ViewHelpers::findHelperFor($method)) {
|
||||||
|
$helper->setView($this);
|
||||||
|
return call_user_func_array(array($helper, $method), $params);
|
||||||
|
} elseif ($this->isNamedPathMethod($method)) {
|
||||||
|
return $this->getNamedPath($method, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception\BadMethodCallException(
|
||||||
|
sprintf("Called to unknown method/helper: %s", $method)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove for 2.0
|
||||||
|
public function __isset($prop)
|
||||||
|
{
|
||||||
|
return $this->localExists($prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function localExists($name)
|
||||||
|
{
|
||||||
|
if ($this->locals instanceof stdClass) {
|
||||||
|
return property_exists($this->locals, $name);
|
||||||
|
} else {
|
||||||
|
return array_key_exists($name, $this->locals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocal($name)
|
||||||
|
{
|
||||||
|
if (!$this->localExists($name)) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Undefined local '%s'", $name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->locals instanceof stdClass) {
|
||||||
|
return $this->locals->$name;
|
||||||
|
} else {
|
||||||
|
return $this->locals[$name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocal($name, $value)
|
||||||
|
{
|
||||||
|
if ($this->locals instanceof stdClass) {
|
||||||
|
$this->locals->$name = $value;
|
||||||
|
} else {
|
||||||
|
$this->locals[$name] = $value;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public function isset_local($name)
|
||||||
|
// {
|
||||||
|
// if ($this->locals instanceof stdClass)
|
||||||
|
// return property_exists($this->locals, $name);
|
||||||
|
// elseif (is_array($this->locals))
|
||||||
|
// return array_key_exists($name, $this->locals);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public function I18n()
|
||||||
|
{
|
||||||
|
return Rails::application()->I18n();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function t($name, array $params = [])
|
||||||
|
{
|
||||||
|
$trans = $this->I18n()->t($name, $params);
|
||||||
|
if (false === $trans) {
|
||||||
|
return '<span class="translation_missing">#' . $name . '</span>';
|
||||||
|
}
|
||||||
|
return $trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method could go somewhere else.
|
||||||
|
*/
|
||||||
|
public function optionsFromEnumColumn($model_name, $column_name, array $extra_options = [])
|
||||||
|
{
|
||||||
|
$options = [];
|
||||||
|
foreach ($model_name::table()->enumValues($column_name) as $val) {
|
||||||
|
$options[$this->humanize($val)] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($extra_options) {
|
||||||
|
$options = array_merge($extra_options, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is meant to be a way to check if
|
||||||
|
* there are contentFor awaiting to be ended.
|
||||||
|
*/
|
||||||
|
public function activeContentFor()
|
||||||
|
{
|
||||||
|
return self::$_content_for_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocals($locals)
|
||||||
|
{
|
||||||
|
if (!is_array($locals) && !$locals instanceof stdClass)
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf('Locals must be either an array or an instance of stdClass, %s passed.', gettype($locals))
|
||||||
|
);
|
||||||
|
$this->locals = $locals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function params()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->parameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->request();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function partial($name, array $locals = array())
|
||||||
|
{
|
||||||
|
$ctrlr_name = Rails::services()->get('inflector')->camelize($this->request()->controller(), false);
|
||||||
|
// $ctrlr_name = substr_replace(Rails::services()->get('inflector')->camelize($ctrlr_name), substr($ctrlr_name, 0, 1), 0, 1);
|
||||||
|
|
||||||
|
if (!isset($locals[$ctrlr_name]) && $this->localExists($ctrlr_name)) {
|
||||||
|
$locals[$ctrlr_name] = $this->getLocal($ctrlr_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!Rails::config()->ar2) {
|
||||||
|
// if (!isset($locals[$name]) && $this->localExists($name)) {
|
||||||
|
// $locals[$name] = $this->getLocal($name);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
$camelized = Rails::services()->get('inflector')->camelize($name, false);
|
||||||
|
if (!isset($locals[$camelized]) && $this->localExists($camelized)) {
|
||||||
|
$locals[$camelized] = $this->getLocal($camelized);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
$base_path = Rails::config()->paths->views;
|
||||||
|
|
||||||
|
if (is_int(strpos($name, '/'))) {
|
||||||
|
$pos = strrpos($name, '/');
|
||||||
|
$name = substr_replace($name, '/_', $pos, 1) . '.php';
|
||||||
|
$filename = $base_path . '/' . $name;
|
||||||
|
} else {
|
||||||
|
if ($namespaces = Rails::application()->dispatcher()->router()->route()->namespaces())
|
||||||
|
$base_path .= '/' . implode('/', $namespaces);
|
||||||
|
$filename = $base_path . '/' . $this->request()->controller() . '/_' . $name . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($locals['collection'])) {
|
||||||
|
$collection = $locals['collection'];
|
||||||
|
unset($locals['collection']);
|
||||||
|
$contents = '';
|
||||||
|
foreach ($collection as $member) {
|
||||||
|
$locals[$name] = $member;
|
||||||
|
$contents .= (new Partial($filename, [], $locals))->render_content();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$partial = new Partial($filename, [], $locals);
|
||||||
|
$contents = $partial->render_content();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActionView/Exception/BadMethodCallException.php
Executable file
6
lib/Rails/ActionView/Exception/BadMethodCallException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Exception;
|
||||||
|
|
||||||
|
class BadMethodCallException extends \Rails\Exception\BadMethodCallException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionView/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionView/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionView/Exception/InvalidArgumentException.php
Executable file
6
lib/Rails/ActionView/Exception/InvalidArgumentException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \Rails\Exception\InvalidArgumentException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionView/Exception/RuntimeException.php
Executable file
6
lib/Rails/ActionView/Exception/RuntimeException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
61
lib/Rails/ActionView/FormBuilder.php
Executable file
61
lib/Rails/ActionView/FormBuilder.php
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
class FormBuilder
|
||||||
|
{
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
|
protected $model;
|
||||||
|
|
||||||
|
protected $inputNamespace;
|
||||||
|
|
||||||
|
public function __construct($helper, $model)
|
||||||
|
{
|
||||||
|
$this->helper = $helper;
|
||||||
|
$this->model = $model;
|
||||||
|
$this->inputNamespace = \Rails::services()->get('inflector')->underscore(get_class($model));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textField($property, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($this->model);
|
||||||
|
return $this->helper->textField($this->inputNamespace, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hiddenField($property, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->hiddenField($this->inputNamespace, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passwordField($property, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->passwordField($this->inputNamespace, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkBox($property, array $attrs = array(), $checked_value = '1', $unchecked_value = '0')
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->passwordField($this->inputNamespace, $property, $attrs, $checked_value, $unchecked_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textArea($property, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->textArea($this->inputNamespace, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function select($property, $options, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->select($this->inputNamespace, $property, $options, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function radioButton($property, $tag_value, array $attrs = array())
|
||||||
|
{
|
||||||
|
$this->helper->setDefaultModel($model);
|
||||||
|
return $this->helper->radioButton($this->inputNamespace, $property, $tag_value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
lib/Rails/ActionView/Helper.php
Executable file
123
lib/Rails/ActionView/Helper.php
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\ActionController;
|
||||||
|
use Rails\ActionView\Helper\Methods;
|
||||||
|
use Rails\Routing\Traits\NamedPathAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some parts of this class was taken from Ruby on Rails helpers.
|
||||||
|
*/
|
||||||
|
abstract class Helper extends ActionView
|
||||||
|
{
|
||||||
|
use NamedPathAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActionView_Base children for methods that
|
||||||
|
* require it when passing Closures, like form().
|
||||||
|
*/
|
||||||
|
private $_view;
|
||||||
|
|
||||||
|
public function __call($method, $params)
|
||||||
|
{
|
||||||
|
if ($this->isNamedPathMethod($method)) {
|
||||||
|
return $this->getNamedPath($method, $params);
|
||||||
|
} elseif ($helper = ViewHelpers::findHelperFor($method)) {
|
||||||
|
$helper->setView($this);
|
||||||
|
return call_user_func_array(array($helper, $method), $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception\BadMethodCallException(
|
||||||
|
sprintf("Called to unknown method/helper: %s", $method)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance of Helper\Base
|
||||||
|
*/
|
||||||
|
public function base()
|
||||||
|
{
|
||||||
|
return ViewHelpers::getBaseHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setView(ActionView $view)
|
||||||
|
{
|
||||||
|
$this->_view = $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
return $this->_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function urlFor($params)
|
||||||
|
{
|
||||||
|
return Rails::application()->router()->urlFor($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function params()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->parameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->request();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function controller()
|
||||||
|
{
|
||||||
|
return Rails::application()->controller();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function u($str)
|
||||||
|
{
|
||||||
|
return urlencode($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hexEncode($str)
|
||||||
|
{
|
||||||
|
$r = '';
|
||||||
|
$e = strlen($str);
|
||||||
|
$c = 0;
|
||||||
|
$h = '';
|
||||||
|
while ($c < $e) {
|
||||||
|
$h = dechex(ord(substr($str, $c++, 1)));
|
||||||
|
while (strlen($h) < 3)
|
||||||
|
$h = '%' . $h;
|
||||||
|
$r .= $h;
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function h($str, $flags = null, $charset = null)
|
||||||
|
{
|
||||||
|
$flags === null && $flags = ENT_COMPAT;
|
||||||
|
!$charset && $charset = Rails::application()->config()->encoding;
|
||||||
|
return htmlspecialchars($str, $flags, $charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function I18n()
|
||||||
|
{
|
||||||
|
return Rails::services()->get('i18n');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function t($name)
|
||||||
|
{
|
||||||
|
return $this->I18n()->t($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: move this method somewhere else, it doesn't belong here.
|
||||||
|
protected function parseUrlParams($url_params)
|
||||||
|
{
|
||||||
|
if ($url_params != '#' && (is_array($url_params) || (strpos($url_params, 'http') !== 0 && strpos($url_params, '/') !== 0))) {
|
||||||
|
if (!is_array($url_params))
|
||||||
|
$url_params = array($url_params);
|
||||||
|
$url_to = Rails::application()->router()->urlFor($url_params, true);
|
||||||
|
} else {
|
||||||
|
$url_to = $url_params;
|
||||||
|
}
|
||||||
|
return $url_to;
|
||||||
|
}
|
||||||
|
}
|
17
lib/Rails/ActionView/Helper/Base.php
Executable file
17
lib/Rails/ActionView/Helper/Base.php
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class shouldn't be extended.
|
||||||
|
* Having all these methods in a separed class will make
|
||||||
|
* possible to override them in other helpers.
|
||||||
|
* Calling one of these methods within a method with the same name
|
||||||
|
* can be done by calling base(), which will return the instance of
|
||||||
|
* this class.
|
||||||
|
*/
|
||||||
|
class Base extends \Rails\ActionView\Helper
|
||||||
|
{
|
||||||
|
use Methods\Form, Methods\Date, Methods\FormTag, Methods\Header,
|
||||||
|
Methods\Html, Methods\Number, Methods\Tag, Methods\Text,
|
||||||
|
Methods\JavaScript, Methods\Inflections, Methods\Assets;
|
||||||
|
}
|
30
lib/Rails/ActionView/Helper/Methods/Assets.php
Executable file
30
lib/Rails/ActionView/Helper/Methods/Assets.php
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Assets
|
||||||
|
{
|
||||||
|
public function assetPath($source, array $options = [])
|
||||||
|
{
|
||||||
|
if (strpos($source, '/') !== 0 && strpos($source, 'http') !== 0) {
|
||||||
|
if (!isset($options['digest'])) {
|
||||||
|
$options['digest'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\Rails::config()->assets->enabled) {
|
||||||
|
if (\Rails::config()->serve_static_assets && $options['digest']) {
|
||||||
|
if ($url = \Rails::assets()->findCompiledFile($source)) {
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file = \Rails::assets()->findFile($source)) {
|
||||||
|
return $file->url();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \Rails::application()->router()->rootPath() . $source;
|
||||||
|
} else {
|
||||||
|
return $source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
lib/Rails/ActionView/Helper/Methods/Date.php
Executable file
62
lib/Rails/ActionView/Helper/Methods/Date.php
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Date
|
||||||
|
{
|
||||||
|
public function timeAgoInWords($fromTime, $includeSeconds = false)
|
||||||
|
{
|
||||||
|
return $this->distanceOfTimeInWords($fromTime, time(), $includeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function distanceOfTimeInWords($fromTime, $toTime = 'now', $includeSeconds = false)
|
||||||
|
{
|
||||||
|
if (!is_int($fromTime)) {
|
||||||
|
$fromTime = strtotime($fromTime);
|
||||||
|
}
|
||||||
|
if (!is_int($toTime)) {
|
||||||
|
$toTime = strtotime($toTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
$distanceInSeconds = round($toTime - $fromTime);
|
||||||
|
|
||||||
|
if ($distanceInSeconds < 0) {
|
||||||
|
$distanceInSeconds = round($fromTime - $toTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
$distanceInMinutes = ceil($distanceInSeconds/60);
|
||||||
|
|
||||||
|
if ($distanceInSeconds < 30)
|
||||||
|
$t = 'less_than_a_minute';
|
||||||
|
elseif ($distanceInSeconds < 90)
|
||||||
|
$t = 'one_minute';
|
||||||
|
elseif ($distanceInSeconds < 2670)
|
||||||
|
$t = ['x_minutes', 't' => $distanceInMinutes];
|
||||||
|
elseif ($distanceInSeconds < 5370)
|
||||||
|
$t = 'about_one_hour';
|
||||||
|
elseif ($distanceInSeconds < 86370)
|
||||||
|
$t = ['about_x_hours', 't' => ceil($distanceInMinutes/60)];
|
||||||
|
elseif ($distanceInSeconds < 151170)
|
||||||
|
$t = 'one_day';
|
||||||
|
elseif ($distanceInSeconds < 2591970)
|
||||||
|
$t = ['x_days', 't' => ceil(($distanceInMinutes/60)/24)];
|
||||||
|
elseif ($distanceInSeconds < 5183970)
|
||||||
|
$t = 'about_one_month';
|
||||||
|
elseif ($distanceInSeconds < 31536059)
|
||||||
|
$t = ['x_months', 't' => ceil((($distanceInMinutes/60)/24)/31)];
|
||||||
|
elseif ($distanceInSeconds < 39312001)
|
||||||
|
$t = 'about_one_year';
|
||||||
|
elseif ($distanceInSeconds < 54864001)
|
||||||
|
$t = 'over_a_year';
|
||||||
|
elseif ($distanceInSeconds < 31536001)
|
||||||
|
$t = 'almost_two_years';
|
||||||
|
else
|
||||||
|
$t = ['about_x_years', 't' => ceil($distanceInMinutes/60/24/365)];
|
||||||
|
|
||||||
|
if (is_array($t))
|
||||||
|
$t[0] = 'actionview.helper.date.' . $t[0];
|
||||||
|
else
|
||||||
|
$t = 'actionview.helper.date.' . $t;
|
||||||
|
|
||||||
|
return $this->t($t);
|
||||||
|
}
|
||||||
|
}
|
224
lib/Rails/ActionView/Helper/Methods/Form.php
Executable file
224
lib/Rails/ActionView/Helper/Methods/Form.php
Executable file
@ -0,0 +1,224 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\Routing\UrlToken;
|
||||||
|
use Rails\ActionController\ActionController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $property may be a method by adding () at the end.
|
||||||
|
* E.g. $this->text_field('artist', 'member_names()');
|
||||||
|
*/
|
||||||
|
trait Form
|
||||||
|
{
|
||||||
|
private $default_model;
|
||||||
|
|
||||||
|
public function formField($type, $model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field($type, $model, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formFor(Rails\ActiveRecord\Base $model, $attrs, \Closure $block = null)
|
||||||
|
{
|
||||||
|
if ($attrs instanceof \Closure) {
|
||||||
|
$block = $attrs;
|
||||||
|
$attrs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($attrs['html']))
|
||||||
|
$attrs['html'] = [];
|
||||||
|
|
||||||
|
if (!isset($attrs['url'])) {
|
||||||
|
|
||||||
|
// if (Rails::config()->ar2) {
|
||||||
|
$className = get_class($model);
|
||||||
|
if (($primaryKey = $className::table()->primaryKey()) && $model->getAttribute($primaryKey)) {
|
||||||
|
$action = 'update';
|
||||||
|
} else {
|
||||||
|
$action = 'create';
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// $action = $model->id ? 'update' : 'create';
|
||||||
|
// }
|
||||||
|
|
||||||
|
$attrs['url'] = ['#' . $action];
|
||||||
|
} else {
|
||||||
|
$token = new UrlToken($attrs['url'][0]);
|
||||||
|
$action = $token->action();
|
||||||
|
}
|
||||||
|
|
||||||
|
$html_attrs = $attrs['html'];
|
||||||
|
|
||||||
|
if (!isset($html_attrs['method'])) {
|
||||||
|
if ($action == 'create')
|
||||||
|
$html_attrs['method'] = 'post';
|
||||||
|
elseif ($action == 'destroy')
|
||||||
|
$html_attrs['method'] = 'delete';
|
||||||
|
else
|
||||||
|
$html_attrs['method'] = 'put';
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check special attribute 'multipart'.
|
||||||
|
if (!empty($html_attrs['multipart'])) {
|
||||||
|
$html_attrs['enctype'] = 'multipart/form-data';
|
||||||
|
unset($html_attrs['multipart']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($html_attrs['method'] != 'post') {
|
||||||
|
$method = $html_attrs['method'];
|
||||||
|
$html_attrs['method'] = 'post';
|
||||||
|
} else {
|
||||||
|
$method = 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
$url_token = new UrlToken($attrs['url'][0]);
|
||||||
|
|
||||||
|
if ($url_token->action() == 'create') {
|
||||||
|
$action_url = Rails::application()->router()->urlFor($url_token->token());
|
||||||
|
} else {
|
||||||
|
list($route, $action_url) = Rails::application()->router()->url_helpers()->find_route_for_token($url_token->token(), $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
$html_attrs['action'] = $action_url;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
if ($method != 'post')
|
||||||
|
echo $this->hiddenFieldTag('_method', $method, ['id' => '']);
|
||||||
|
|
||||||
|
$block(new \Rails\ActionView\FormBuilder($this, $model));
|
||||||
|
|
||||||
|
return $this->contentTag('form', ob_get_clean(), $html_attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textField($model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field('text', $model, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hiddenField($model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field('hidden', $model, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passwordField($model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field('password', $model, $property, array_merge($attrs, ['value' => '']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkBox($model, $property, array $attrs = array(), $checked_value = '1', $unchecked_value = '0')
|
||||||
|
{
|
||||||
|
if ($this->_get_model_property($model, $property))
|
||||||
|
$attrs['checked'] = 'checked';
|
||||||
|
|
||||||
|
$attrs['value'] = $checked_value;
|
||||||
|
|
||||||
|
$hidden = $this->tag('input', array('type' => 'hidden', 'name' => $model.'['.$property.']', 'value' => $unchecked_value));
|
||||||
|
|
||||||
|
$check_box = $this->_form_field('checkbox', $model, $property, $attrs);
|
||||||
|
|
||||||
|
return $hidden . "\n" . $check_box;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textArea($model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
if (isset($attrs['size']) && is_int(strpos($attrs['size'], 'x'))) {
|
||||||
|
list($attrs['cols'], $attrs['rows']) = explode('x', $attrs['size']);
|
||||||
|
unset($attrs['size']);
|
||||||
|
}
|
||||||
|
return $this->_form_field('textarea', $model, $property, $attrs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function select($model, $property, $options, array $attrs = array())
|
||||||
|
{
|
||||||
|
if (!is_string($options)) {
|
||||||
|
$value = $this->_get_model_property($model, $property);
|
||||||
|
$options = $this->optionsForSelect($options, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($attrs['prompt'])) {
|
||||||
|
$options = $this->contentTag('option', $attrs['prompt'], ['value' => '', 'allow_blank_attrs' => true]) . "\n" . $options;
|
||||||
|
unset($attrs['prompt']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$attrs['value'] = $options;
|
||||||
|
|
||||||
|
return $this->_form_field('select', $model, $property, $attrs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function radioButton($model, $property, $tag_value, array $attrs = array())
|
||||||
|
{
|
||||||
|
(string)$this->_get_model_property($model, $property) == (string)$tag_value && $attrs['checked'] = 'checked';
|
||||||
|
$attrs['value'] = $tag_value;
|
||||||
|
return $this->_form_field('radio', $model, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fileField($model, $property, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field('file', $model, $property, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDefaultModel(\Rails\ActiveRecord\Base $model)
|
||||||
|
{
|
||||||
|
$this->default_model = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _form_field($field_type, $model, $property, array $attrs = array(), $content_tag = false)
|
||||||
|
{
|
||||||
|
$value = array_key_exists('value', $attrs) ? $attrs['value'] : $this->_get_model_property($model, $property);
|
||||||
|
|
||||||
|
# Note here that the name tag attribute is forced to be underscored.
|
||||||
|
$underscoreProperty = preg_match('/[A-Z]/', $property) ?
|
||||||
|
Rails::services()->get('inflector')->underscore($property) : $property;
|
||||||
|
|
||||||
|
$attrs['name'] = $model.'['.$underscoreProperty.']';
|
||||||
|
|
||||||
|
if (!isset($attrs['id']))
|
||||||
|
$attrs['id'] = $model . '_' . $underscoreProperty;
|
||||||
|
|
||||||
|
if ($content_tag) {
|
||||||
|
unset($attrs['value']);
|
||||||
|
return $this->contentTag($field_type, $value, $attrs);
|
||||||
|
} else {
|
||||||
|
$attrs['type'] = $field_type;
|
||||||
|
if ($value !== '')
|
||||||
|
$attrs['value'] = $value;
|
||||||
|
return $this->tag('input', $attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _get_model_property($model, $property)
|
||||||
|
{
|
||||||
|
$value = '';
|
||||||
|
$mdl = false;
|
||||||
|
|
||||||
|
if ($this->default_model) {
|
||||||
|
$mdl = $this->default_model;
|
||||||
|
} else {
|
||||||
|
$vars = Rails::application()->dispatcher()->controller()->vars();
|
||||||
|
if (!empty($vars->$model)) {
|
||||||
|
$mdl = $vars->$model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($mdl) {
|
||||||
|
// if (!Rails::config()->ar2) {
|
||||||
|
if ($mdl->isAttribute($property)) {
|
||||||
|
$value = (string)$mdl->$property;
|
||||||
|
} elseif (($modelProps = get_class_vars(get_class($mdl))) && array_key_exists($property, $modelProps)) {
|
||||||
|
$value = (string)$mdl->$property;
|
||||||
|
} else {
|
||||||
|
# It's assumed this is a method.
|
||||||
|
$value = (string)$mdl->$property();
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// /**
|
||||||
|
// *
|
||||||
|
// */
|
||||||
|
// $value = (string)$mdl->$property();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->default_model = null;
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
219
lib/Rails/ActionView/Helper/Methods/FormTag.php
Executable file
219
lib/Rails/ActionView/Helper/Methods/FormTag.php
Executable file
@ -0,0 +1,219 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionController\ActionController;
|
||||||
|
use Rails\ActionView\Exception;
|
||||||
|
use Rails\Toolbox\ArrayTools;
|
||||||
|
|
||||||
|
trait FormTag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Passing an empty value as $action_url will cause the form
|
||||||
|
* to omit the "action" attribute, causing the form to be
|
||||||
|
* submitted to the current uri.
|
||||||
|
*
|
||||||
|
* To avoid passing an empty array for $attrs,
|
||||||
|
* pass a Closure as second argument and it
|
||||||
|
* will be taken as $block.
|
||||||
|
*
|
||||||
|
* Likewise, passing Closure as first argument
|
||||||
|
* (meaning the form will be submitted to the current url)
|
||||||
|
* will work too, instead of passing an empty value as
|
||||||
|
* $action_url.
|
||||||
|
*
|
||||||
|
* Note that if $action_url is an array like [ 'controller' => 'ctrl', 'action' => ... ],
|
||||||
|
* it will be taken as $attrs. Therefore the action url should be passed as [ 'ctrl#action', ... ].
|
||||||
|
*/
|
||||||
|
public function formTag($action_url = null, $attrs = [], Closure $block = null)
|
||||||
|
{
|
||||||
|
if (func_num_args() == 1 && $action_url instanceof Closure) {
|
||||||
|
$block = $action_url;
|
||||||
|
$action_url = null;
|
||||||
|
} elseif (func_num_args() == 2 && is_array($action_url) && is_string(key($action_url))) {
|
||||||
|
$block = $attrs;
|
||||||
|
$attrs = $action_url;
|
||||||
|
$action_url = null;
|
||||||
|
} elseif ($attrs instanceof Closure) {
|
||||||
|
$block = $attrs;
|
||||||
|
$attrs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$block instanceof Closure)
|
||||||
|
throw new Exception\BadMethodCallException("One of the arguments must be a Closure");
|
||||||
|
|
||||||
|
if (empty($attrs['method'])) {
|
||||||
|
$attrs['method'] = 'post';
|
||||||
|
$method = 'post';
|
||||||
|
} elseif (($method = strtolower($attrs['method'])) != 'get') {
|
||||||
|
$attrs['method'] = 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check special attribute 'multipart'.
|
||||||
|
if (!empty($attrs['multipart'])) {
|
||||||
|
$attrs['enctype'] = 'multipart/form-data';
|
||||||
|
unset($attrs['multipart']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action_url)
|
||||||
|
$attrs['action'] = Rails::application()->router()->urlFor($action_url);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
if ($method != 'get' && $method != 'post')
|
||||||
|
echo $this->hiddenFieldTag('_method', $method, ['id' => '']);
|
||||||
|
$block();
|
||||||
|
return $this->contentTag('form', ob_get_clean(), $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formFieldTag($type, $name, $value = null, array $attrs = [])
|
||||||
|
{
|
||||||
|
return $this->_form_field_tag($type, $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submitTag($value, array $attrs = [])
|
||||||
|
{
|
||||||
|
$attrs['type'] = 'submit';
|
||||||
|
$attrs['value'] = $value;
|
||||||
|
!isset($attrs['name']) && $attrs['name'] = 'commit';
|
||||||
|
return $this->tag('input', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textFieldTag($name, $value = null, array $attrs = [])
|
||||||
|
{
|
||||||
|
if (is_array($value)) {
|
||||||
|
$attrs = $value;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
return $this->_form_field_tag('text', $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hiddenFieldTag($name, $value, array $attrs = [])
|
||||||
|
{
|
||||||
|
return $this->_form_field_tag('hidden', $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passwordFieldTag($name, $value = null, array $attrs = array())
|
||||||
|
{
|
||||||
|
return $this->_form_field_tag('password', $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkBoxTag($name, $value = '1', $checked = false, array $attrs = [])
|
||||||
|
{
|
||||||
|
if ($checked) {
|
||||||
|
$attrs['checked'] = 'checked';
|
||||||
|
}
|
||||||
|
return $this->_form_field_tag('checkbox', $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textAreaTag($name, $value, array $attrs = [])
|
||||||
|
{
|
||||||
|
if (isset($attrs['size']) && is_int(strpos($attrs['size'], 'x'))) {
|
||||||
|
list($attrs['cols'], $attrs['rows']) = explode('x', $attrs['size']);
|
||||||
|
unset($attrs['size']);
|
||||||
|
}
|
||||||
|
return $this->_form_field_tag('textarea', $name, $value, $attrs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function radioButtonTag($name, $value, $checked = false, array $attrs = [])
|
||||||
|
{
|
||||||
|
if ($checked)
|
||||||
|
$attrs['checked'] = 'checked';
|
||||||
|
return $this->_form_field_tag('radio', $name, $value, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
# $options may be closure, collection or an array of name => values.
|
||||||
|
public function selectTag($name, $options, array $attrs = [])
|
||||||
|
{
|
||||||
|
# This is found also in Form::select()
|
||||||
|
if (!is_string($options)) {
|
||||||
|
if (is_array($options) && ArrayTools::isIndexed($options) && count($options) == 2)
|
||||||
|
list ($options, $value) = $options;
|
||||||
|
else
|
||||||
|
$value = null;
|
||||||
|
$options = $this->optionsForSelect($options, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($attrs['prompt'])) {
|
||||||
|
$options = $this->contentTag('option', $attrs['prompt'], ['value' => '', 'allow_blank_attrs' => true]) . "\n" . $options;
|
||||||
|
unset($attrs['prompt']);
|
||||||
|
}
|
||||||
|
$attrs['value'] = $attrs['type'] = null;
|
||||||
|
|
||||||
|
$select_tag = $this->_form_field_tag('select', $name, $options, $attrs, true);
|
||||||
|
return $select_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: For options to recognize the $tag_value, it must be identical to the option's value.
|
||||||
|
*/
|
||||||
|
public function optionsForSelect($options, $tag_value = null)
|
||||||
|
{
|
||||||
|
# New feature: accept anonymous functions that will return options.
|
||||||
|
if ($options instanceof Closure) {
|
||||||
|
$options = $options();
|
||||||
|
/**
|
||||||
|
* New feature: accept collection as option in index 0, in index 1 the option name and in index 2 the value
|
||||||
|
* which are the properties of the models that will be used.
|
||||||
|
* Example: [ Category::all(), 'name', 'id' ]
|
||||||
|
* The second and third indexes can be either:
|
||||||
|
* - An attribute name
|
||||||
|
* - A public property
|
||||||
|
* - A method of the model that will return the value for the option/name
|
||||||
|
*/
|
||||||
|
} elseif (is_array($options) && count($options) == 3 && ArrayTools::isIndexed($options) && $options[0] instanceof \Rails\ActiveModel\Collection) {
|
||||||
|
list($models, $optionName, $valueName) = $options;
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
if ($models->any()) {
|
||||||
|
$modelClass = get_class($models[0]);
|
||||||
|
|
||||||
|
foreach ($models as $m) {
|
||||||
|
if ($modelClass::isAttribute($optionName)) {
|
||||||
|
$option = $m->getAttribute($optionName);
|
||||||
|
} elseif ($modelClass::hasPublicProperty($optionName)) {
|
||||||
|
$option = $m->$optionName;
|
||||||
|
} else {
|
||||||
|
# We assume it's a method.
|
||||||
|
$option = $m->$optionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modelClass::isAttribute($valueName)) {
|
||||||
|
$value = $m->getAttribute($valueName);
|
||||||
|
} elseif ($modelClass::hasPublicProperty($optionName)) {
|
||||||
|
$option = $m->$optionName;
|
||||||
|
} else {
|
||||||
|
# We assume it's a method.
|
||||||
|
$value = $m->$valueName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$options[$option] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tag_value = (string)$tag_value;
|
||||||
|
|
||||||
|
$tags = [];
|
||||||
|
foreach ($options as $name => $value) {
|
||||||
|
$value = (string)$value;
|
||||||
|
$tags[] = $this->_form_field_tag('option', null, $name, array('selected' => $value === $tag_value ? '1' : '', 'id' => '', 'value' => $value), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _form_field_tag($field_type, $name = null, $value, array $attrs = [], $content_tag = false)
|
||||||
|
{
|
||||||
|
!isset($attrs['id']) && $attrs['id'] = trim(str_replace(['[', ']', '()', '__'], ['_', '_', '', '_'], $name), '_');
|
||||||
|
$name && $attrs['name'] = $name;
|
||||||
|
$value = (string)$value;
|
||||||
|
|
||||||
|
if ($content_tag) {
|
||||||
|
return $this->contentTag($field_type, $value, $attrs);
|
||||||
|
} else {
|
||||||
|
$attrs['type'] = $field_type;
|
||||||
|
$value !== '' && $attrs['value'] = $value;
|
||||||
|
return $this->tag('input', $attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
lib/Rails/ActionView/Helper/Methods/Header.php
Executable file
95
lib/Rails/ActionView/Helper/Methods/Header.php
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
trait Header
|
||||||
|
{
|
||||||
|
public function stylesheetLinkTag($url, array $attrs = array())
|
||||||
|
{
|
||||||
|
empty($attrs['type']) && $attrs['type'] = 'text/css';
|
||||||
|
empty($attrs['rel']) && $attrs['rel'] = 'stylesheet';
|
||||||
|
|
||||||
|
$assets_config = Rails::application()->config()->assets;
|
||||||
|
|
||||||
|
# If assets are enabled and fails to find the wanted file, normal behaviour of
|
||||||
|
# this method will be executed.
|
||||||
|
if ($assets_config->enabled) {
|
||||||
|
if (Rails::application()->config()->serve_static_assets) {
|
||||||
|
if ($fileUrl = Rails::assets()->findCompiledFile($url . '.css')) {
|
||||||
|
// $attrs['href'] = Rails::assets()->prefix() . '/' . $file;
|
||||||
|
$attrs['href'] = $fileUrl;
|
||||||
|
return $this->tag('link', $attrs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$asset_file = $url . '.css';
|
||||||
|
if ($assets_config->concat) {
|
||||||
|
if ($href = Rails::assets()->getFileUrl($asset_file )) {
|
||||||
|
$attrs['href'] = $href;
|
||||||
|
return $this->tag('link', $attrs);
|
||||||
|
}
|
||||||
|
} elseif ($paths = Rails::assets()->getFileUrls($asset_file)) {
|
||||||
|
$tags = [];
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
$attrs['href'] = $path;
|
||||||
|
$tags[] = $this->tag('link', $attrs);
|
||||||
|
}
|
||||||
|
return implode("\n", $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$attrs['href'] = $this->_parse_url($url, '/stylesheets/', 'css');
|
||||||
|
return $this->tag('link', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function javascriptIncludeTag($url, array $attrs = array())
|
||||||
|
{
|
||||||
|
empty($attrs['type']) && $attrs['type'] = 'text/javascript';
|
||||||
|
|
||||||
|
$assets_config = Rails::application()->config()->assets;
|
||||||
|
|
||||||
|
# If assets are enabled and fails to find the wanted file, normal behaviour of
|
||||||
|
# this method will be executed.
|
||||||
|
if ($assets_config->enabled) {
|
||||||
|
if (Rails::application()->config()->serve_static_assets) {
|
||||||
|
if ($fileUrl = Rails::assets()->findCompiledFile($url . '.js')) {
|
||||||
|
// $attrs['src'] = Rails::assets()->prefix() . '/' . $file;
|
||||||
|
$attrs['src'] = $fileUrl;
|
||||||
|
return $this->contentTag('script', '', $attrs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$asset_file = $url . '.js';
|
||||||
|
if ($assets_config->concat) {
|
||||||
|
if ($src = Rails::assets()->getFileUrl($asset_file)) {
|
||||||
|
$attrs['src'] = $src;
|
||||||
|
return $this->contentTag('script', '', $attrs);
|
||||||
|
}
|
||||||
|
} elseif ($paths = Rails::assets()->getFileUrls($asset_file)) {
|
||||||
|
$tags = [];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
$attrs['src'] = $path;
|
||||||
|
$tags[] = $this->contentTag('script', '', $attrs);
|
||||||
|
}
|
||||||
|
return implode("\n", $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$attrs['src'] = $this->_parse_url($url, '/javascripts/', 'js');
|
||||||
|
return $this->contentTag('script', '', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _parse_url($url, $default_base_url, $ext)
|
||||||
|
{
|
||||||
|
$base_path = Rails::application()->router()->basePath();
|
||||||
|
|
||||||
|
if (strpos($url, '/') === 0) {
|
||||||
|
$url = $base_path . $url;
|
||||||
|
} elseif (strpos($url, 'http') !== 0 && strpos($url, 'www') !== 0) {
|
||||||
|
$url = $base_path . $default_base_url . $url . '.' . $ext;
|
||||||
|
}
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
}
|
114
lib/Rails/ActionView/Helper/Methods/Html.php
Executable file
114
lib/Rails/ActionView/Helper/Methods/Html.php
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
trait Html
|
||||||
|
{
|
||||||
|
private $_form_attrs;
|
||||||
|
|
||||||
|
public function linkTo($link, $url_params, array $attrs = array())
|
||||||
|
{
|
||||||
|
$url_to = $this->parseUrlParams($url_params);
|
||||||
|
$onclick = '';
|
||||||
|
|
||||||
|
if (isset($attrs['method'])) {
|
||||||
|
$onclick = "var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'post';";
|
||||||
|
|
||||||
|
if ($attrs['method'] != 'post') {
|
||||||
|
$onclick .= "var m = document.createElement('input'); m.type = 'hidden'; m.name = '_method'; m.value = '".$attrs['method']."'; f.appendChild(m);";
|
||||||
|
}
|
||||||
|
|
||||||
|
$onclick .= "f.action = this.href;f.submit();return false;";
|
||||||
|
// $attrs['data-method'] = $attrs['method'];
|
||||||
|
unset($attrs['method']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($attrs['confirm'])) {
|
||||||
|
if (!$onclick)
|
||||||
|
$onclick = "if (!confirm('".$attrs['confirm']."')) return false;";
|
||||||
|
else
|
||||||
|
$onclick = 'if (confirm(\''.$attrs['confirm'].'\')) {'.$onclick.'}; return false;';
|
||||||
|
unset($attrs['confirm']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($onclick)
|
||||||
|
$attrs['onclick'] = $onclick;
|
||||||
|
|
||||||
|
$attrs['href'] = $url_to;
|
||||||
|
|
||||||
|
return $this->contentTag('a', $link, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function linkToIf($condition, $link, $url_params, array $attrs = array())
|
||||||
|
{
|
||||||
|
if ($condition)
|
||||||
|
return $this->linkTo($link, $url_params, $attrs);
|
||||||
|
else
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autoDiscoveryLinkTag($type = 'rss', $url_params = null, array $attrs = array())
|
||||||
|
{
|
||||||
|
if (!$url_params) {
|
||||||
|
$url_params = Rails::application()->dispatcher()->router()->route()->controller . '#' .
|
||||||
|
Rails::application()->dispatcher()->router()->route()->action;
|
||||||
|
}
|
||||||
|
$attrs['href'] = $this->parseUrlParams($url_params);
|
||||||
|
|
||||||
|
empty($attrs['type']) && $attrs['type'] = 'application/' . strtolower($type) . '+xml';
|
||||||
|
empty($attrs['title']) && $attrs['title'] = strtoupper($type);
|
||||||
|
|
||||||
|
return $this->tag('link', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function imageTag($source, array $attrs = array())
|
||||||
|
{
|
||||||
|
$source = $this->assetPath($source);
|
||||||
|
|
||||||
|
if (!isset($attrs['alt']))
|
||||||
|
$attrs['alt'] = $this->humanize(pathinfo($source, PATHINFO_FILENAME));
|
||||||
|
if (isset($attrs['size']))
|
||||||
|
$this->_parse_size($attrs);
|
||||||
|
$attrs['src'] = $source;
|
||||||
|
return $this->tag('img', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mailTo($address, $name = null, array $options = array())
|
||||||
|
{
|
||||||
|
if ($name === null) {
|
||||||
|
$name = $address;
|
||||||
|
if (isset($options['replace_at']))
|
||||||
|
$name = str_replace('@', $options['replace_at'], $address);
|
||||||
|
if (isset($options['replace_dot']))
|
||||||
|
$name = str_replace('.', $options['replace_dot'], $address);
|
||||||
|
}
|
||||||
|
$encode = isset($options['encode']) ? $options['encode'] : false;
|
||||||
|
|
||||||
|
if ($encode == 'hex') {
|
||||||
|
$address = $this->hexEncode($address . '.');
|
||||||
|
$address = str_replace(['%40', '%2e'], ['@', '.'], $address);
|
||||||
|
}
|
||||||
|
|
||||||
|
$address_options = array('subject', 'body', 'cc', 'bcc');
|
||||||
|
|
||||||
|
$query = array_intersect_key($options, array_fill_keys($address_options, null));
|
||||||
|
if ($query)
|
||||||
|
$query = '?' . http_build_query($query);
|
||||||
|
else
|
||||||
|
$query = '';
|
||||||
|
|
||||||
|
$address .= $query;
|
||||||
|
|
||||||
|
$attrs = array_diff_key($options, $address_options, array_fill_keys(array('replace_at', 'replace_dot', 'encode'), null));
|
||||||
|
$attrs['href'] = 'mailto:' . $address;
|
||||||
|
|
||||||
|
$tag = $this->contentTag('a', $name, $attrs);
|
||||||
|
|
||||||
|
if ($encode = 'javascript') {
|
||||||
|
$tag = "document.write('" . $tag . "');";
|
||||||
|
return $this->javascriptTag('eval(decodeURIComponent(\'' . $this->hexEncode($tag) . '\'))');
|
||||||
|
} else
|
||||||
|
return $tag;
|
||||||
|
}
|
||||||
|
}
|
55
lib/Rails/ActionView/Helper/Methods/Inflections.php
Executable file
55
lib/Rails/ActionView/Helper/Methods/Inflections.php
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Inflections
|
||||||
|
{
|
||||||
|
public function pluralize($word, $locale = 'en')
|
||||||
|
{
|
||||||
|
return $this->inflector()->pluralize($word, $locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function singularize($word, $locale = 'en')
|
||||||
|
{
|
||||||
|
return $this->inflector()->singularize($word, $locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function camelize($term, $uppercaseFirstLetter = true)
|
||||||
|
{
|
||||||
|
return $this->inflector()->camelize($term, $uppercaseFirstLetter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function underscore($camelCasedWord)
|
||||||
|
{
|
||||||
|
return $this->inflector()->underscore($camelCasedWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function humanize($lowerCaseAndUnderscoredWord)
|
||||||
|
{
|
||||||
|
return $this->inflector()->humanize($lowerCaseAndUnderscoredWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function titleize($word)
|
||||||
|
{
|
||||||
|
return $this->inflector()->titleize($word);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tableize($className)
|
||||||
|
{
|
||||||
|
return $this->inflector()->tableize($className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function classify($tableName)
|
||||||
|
{
|
||||||
|
return $this->inflector()->classify($tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ordinal($number)
|
||||||
|
{
|
||||||
|
return $this->inflector()->ordinal($number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function inflector()
|
||||||
|
{
|
||||||
|
return \Rails::services()->get('inflector');
|
||||||
|
}
|
||||||
|
}
|
91
lib/Rails/ActionView/Helper/Methods/JavaScript.php
Executable file
91
lib/Rails/ActionView/Helper/Methods/JavaScript.php
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait JavaScript
|
||||||
|
{
|
||||||
|
static private $JS_ESCAPE_MAP = [
|
||||||
|
'\\' => '\\\\',
|
||||||
|
'</' => '<\/',
|
||||||
|
"\r\n" => '\n',
|
||||||
|
"\n" => '\n',
|
||||||
|
"\r" => '\n',
|
||||||
|
'"' => '\\"',
|
||||||
|
"'" => "\\'"
|
||||||
|
];
|
||||||
|
|
||||||
|
static private $JSON_ESCAPE = [
|
||||||
|
'&' => '\u0026',
|
||||||
|
'>' => '\u003E',
|
||||||
|
'<' => '\u003C'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function linkToFunction($link, $function, array $attrs = array())
|
||||||
|
{
|
||||||
|
$attrs['href'] = '#';
|
||||||
|
if ($function) {
|
||||||
|
$function = trim($function);
|
||||||
|
if (strpos($function, -1) != ';')
|
||||||
|
$function .= ';';
|
||||||
|
$function .= ' return false;';
|
||||||
|
} else
|
||||||
|
$function = 'return false;';
|
||||||
|
$attrs['onclick'] = $function;
|
||||||
|
|
||||||
|
return $this->contentTag('a', $link, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buttonToFunction($name, $function, array $attrs = array())
|
||||||
|
{
|
||||||
|
$attrs['href'] = '#';
|
||||||
|
$function = trim($function);
|
||||||
|
if (strpos($function, -1) != ';')
|
||||||
|
$function .= ';';
|
||||||
|
$function .= ' return false;';
|
||||||
|
$attrs['onclick'] = $function;
|
||||||
|
$attrs['type'] = 'button';
|
||||||
|
$attrs['value'] = $name;
|
||||||
|
return $this->tag('input', $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function j($str)
|
||||||
|
{
|
||||||
|
return $this->escapeJavascript($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeJavascript($str)
|
||||||
|
{
|
||||||
|
return str_replace(array_keys(self::$JS_ESCAPE_MAP), self::$JS_ESCAPE_MAP, $str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $this->javascriptTag('alert("foo")', ['defer' => 'defer']);
|
||||||
|
* $this->javascriptTag(['defer' => 'defer'], function() { ... });
|
||||||
|
* $this->javascriptTag(function() { ... });
|
||||||
|
*/
|
||||||
|
public function javascriptTag($contentOrOptionWithBlock, $htmlOptions = null)
|
||||||
|
{
|
||||||
|
if ($contentOrOptionWithBlock instanceof \Closure) {
|
||||||
|
ob_start();
|
||||||
|
$contentOrOptionWithBlock();
|
||||||
|
$contents = ob_get_clean();
|
||||||
|
$htmlOptions = [];
|
||||||
|
} elseif ($htmlOptions instanceof \Closure) {
|
||||||
|
ob_start();
|
||||||
|
$htmlOptions();
|
||||||
|
$contents = ob_get_clean();
|
||||||
|
$htmlOptions = $contentOrOptionWithBlock;
|
||||||
|
} elseif (is_string($contentOrOptionWithBlock)) {
|
||||||
|
$contents = $contentOrOptionWithBlock;
|
||||||
|
if (!is_array($htmlOptions)) {
|
||||||
|
$htmlOptions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->contentTag('script', $contents, $htmlOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonEscape($str)
|
||||||
|
{
|
||||||
|
return str_replace(array_keys(self::$JSON_ESCAPE), self::$JSON_ESCAPE, $str);
|
||||||
|
}
|
||||||
|
}
|
26
lib/Rails/ActionView/Helper/Methods/Number.php
Executable file
26
lib/Rails/ActionView/Helper/Methods/Number.php
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Number
|
||||||
|
{
|
||||||
|
public function numberToHumanSize($number, array $options = array())
|
||||||
|
{
|
||||||
|
$size = $number / 1024;
|
||||||
|
if ($size < 1024) {
|
||||||
|
$size = number_format($size, 1);
|
||||||
|
$size .= ' KB';
|
||||||
|
} else {
|
||||||
|
if (($size = ($size / 1024)) < 1024) {
|
||||||
|
$size = number_format($size, 1);
|
||||||
|
$size .= ' MB';
|
||||||
|
} elseif (($size = ($size / 1024)) < 1024) {
|
||||||
|
$size = number_format($size, 1);
|
||||||
|
$size .= ' GB';
|
||||||
|
} elseif (($size = ($size / 1024)) < 1024) {
|
||||||
|
$size = number_format($size, 1);
|
||||||
|
$size .= ' TB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
}
|
62
lib/Rails/ActionView/Helper/Methods/Tag.php
Executable file
62
lib/Rails/ActionView/Helper/Methods/Tag.php
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Tag
|
||||||
|
{
|
||||||
|
public function tag($name, array $options = array(), $open = false, $escape = false)
|
||||||
|
{
|
||||||
|
return '<' . $name . ' ' . $this->_options($options, $escape) . ($open ? '>' : ' />');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contentTag($name, $content, array $options = array(), $escape = false)
|
||||||
|
{
|
||||||
|
if ($content instanceof \Closure) {
|
||||||
|
$content = $content($this->view());
|
||||||
|
}
|
||||||
|
return $this->_content_tag_string($name, $content, $options, $escape);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _options(array $options = array(), $escape = false)
|
||||||
|
{
|
||||||
|
$opts = array();
|
||||||
|
if (isset($options['allow_blank_attrs'])) {
|
||||||
|
$allow_blank_attrs = true;
|
||||||
|
unset($options['allow_blank_attrs']);
|
||||||
|
} else
|
||||||
|
$allow_blank_attrs = false;
|
||||||
|
|
||||||
|
foreach ($options as $opt => $val) {
|
||||||
|
# "class" attribute allows array.
|
||||||
|
if ($opt == 'class' && is_array($val)) {
|
||||||
|
$val = implode(' ', \Rails\Toolbox\ArrayTools::flatten($val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($val))
|
||||||
|
$val = implode(' ', $val);
|
||||||
|
|
||||||
|
if ((string)$val === '' && !$allow_blank_attrs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_int($opt))
|
||||||
|
$opts[] = $val;
|
||||||
|
else {
|
||||||
|
$escape && $val = htmlentities($val);
|
||||||
|
$opts[] = $opt . '="' . $val . '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode(' ', $opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _parse_size(&$attrs)
|
||||||
|
{
|
||||||
|
if (is_int(strpos($attrs['size'], 'x'))) {
|
||||||
|
list ($attrs['width'], $attrs['height']) = explode('x', $attrs['size']);
|
||||||
|
unset($attrs['size']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _content_tag_string($name, $content, array $options, $escape = false)
|
||||||
|
{
|
||||||
|
return '<' . $name . ' ' . $this->_options($options) . '>' . ($escape ? $this->h($content) : $content) . '</' . $name . '>';
|
||||||
|
}
|
||||||
|
}
|
33
lib/Rails/ActionView/Helper/Methods/Text.php
Executable file
33
lib/Rails/ActionView/Helper/Methods/Text.php
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\Methods;
|
||||||
|
|
||||||
|
trait Text
|
||||||
|
{
|
||||||
|
private $_cycle_count = 0,
|
||||||
|
$_cycle_vars = [];
|
||||||
|
|
||||||
|
public function cycle()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
|
||||||
|
# Clear vars if null was passed.
|
||||||
|
if (count($args) == 1 && $args[0] === []) {
|
||||||
|
$this->_cycle_vars = [];
|
||||||
|
$this->_cycle_count = 0;
|
||||||
|
return;
|
||||||
|
# Reset cycle if new options were given.
|
||||||
|
} elseif ($this->_cycle_vars && $this->_cycle_vars !== $args) {
|
||||||
|
$this->_cycle_vars = [];
|
||||||
|
$this->_cycle_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->_cycle_vars))
|
||||||
|
$this->_cycle_vars = $args;
|
||||||
|
if ($this->_cycle_count > count($this->_cycle_vars) - 1)
|
||||||
|
$this->_cycle_count = 0;
|
||||||
|
|
||||||
|
$value = $this->_cycle_vars[$this->_cycle_count];
|
||||||
|
$this->_cycle_count++;
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
168
lib/Rails/ActionView/Helper/WillPaginate.delete/AbstractRenderer.php
Executable file
168
lib/Rails/ActionView/Helper/WillPaginate.delete/AbstractRenderer.php
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\WillPaginate;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Collection;
|
||||||
|
|
||||||
|
abstract class AbstractRenderer
|
||||||
|
{
|
||||||
|
protected $collection;
|
||||||
|
|
||||||
|
protected $options;
|
||||||
|
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
|
protected $pages;
|
||||||
|
|
||||||
|
protected $page;
|
||||||
|
|
||||||
|
protected $url;
|
||||||
|
|
||||||
|
public function __construct($helper, Collection $collection, array $options = [])
|
||||||
|
{
|
||||||
|
$this->collection = $collection;
|
||||||
|
$this->helper = $helper;
|
||||||
|
$this->options = array_merge($this->defaultOptions(), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toHtml()
|
||||||
|
{
|
||||||
|
$html = implode(array_map(function($item){
|
||||||
|
if (is_int($item))
|
||||||
|
return $this->pageNumber($item);
|
||||||
|
else
|
||||||
|
return $this->$item();
|
||||||
|
}, $this->pagination()), $this->options['link_separator']);
|
||||||
|
|
||||||
|
return $this->options['container'] ? $this->htmlContainer($html) : $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function defaultOptions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'previous_label' => '← ' . $this->helper->t('actionview.helper.will_paginate.previous'),
|
||||||
|
'next_label' => $this->helper->t('actionview.helper.will_paginate.next') . ' →',
|
||||||
|
'container' => true,
|
||||||
|
'link_separator' => ' '
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pageNumber($page)
|
||||||
|
{
|
||||||
|
if ($page != $this->collection->currentPage())
|
||||||
|
return $this->link($page, $page, ['rel' => $this->relValue($page)]);
|
||||||
|
else
|
||||||
|
return $this->tag('span', $page, ['class' => 'current']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function gap()
|
||||||
|
{
|
||||||
|
return '<span class="gap">…</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function previousPage()
|
||||||
|
{
|
||||||
|
$num = $this->collection->currentPage() > 1 ?
|
||||||
|
$this->collection->currentPage() - 1 : false;
|
||||||
|
return $this->previousOrNextPage($num, $this->options['previous_label'], 'previousPage');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function nextPage()
|
||||||
|
{
|
||||||
|
$num = $this->collection->currentPage() < $this->collection->totalPages() ?
|
||||||
|
$this->collection->currentPage() + 1 : false;
|
||||||
|
return $this->previousOrNextPage($num, $this->options['next_label'], 'nextPage');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function previousOrNextPage($page, $text, $classname)
|
||||||
|
{
|
||||||
|
if ($page)
|
||||||
|
return $this->link($text, $page, ['class' => $classname]);
|
||||||
|
else
|
||||||
|
return $this->tag('span', $text, ['class' => $classname . ' disabled']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function htmlContainer($html)
|
||||||
|
{
|
||||||
|
return $this->tag('div', $html, $this->containerAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function containerAttributes()
|
||||||
|
{
|
||||||
|
return ['class' => 'pagination'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function relValue($page)
|
||||||
|
{
|
||||||
|
if ($this->collection->currentPage() - 1 == $page)
|
||||||
|
return 'prev' . ($page == 1 ? ' start' : '');
|
||||||
|
elseif ($this->collection->currentPage() + 1 == $page)
|
||||||
|
return 'next';
|
||||||
|
elseif ($page == 1)
|
||||||
|
return 'start';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pagination()
|
||||||
|
{
|
||||||
|
$pages = $this->collection->totalPages();
|
||||||
|
$page = $this->collection->currentPage();
|
||||||
|
$pagination = [];
|
||||||
|
|
||||||
|
$pagination[] = 'previousPage';
|
||||||
|
$pagination[] = 1;
|
||||||
|
|
||||||
|
if ($pages < 10){
|
||||||
|
for ($i = 2; $i <= $pages; $i++){
|
||||||
|
$pagination[] = $i;
|
||||||
|
}
|
||||||
|
} elseif ($page > ($pages - 4)) {
|
||||||
|
$pagination[] = 'gap';
|
||||||
|
for ($i = ($pages - 4); $i < ($pages); $i++) {
|
||||||
|
$pagination[] = $i;
|
||||||
|
}
|
||||||
|
} elseif ($page > 4) {
|
||||||
|
$pagination[] = 'gap';
|
||||||
|
for ($i = ($page - 1); $i <= ($page + 2); $i++) {
|
||||||
|
$pagination[] = $i;
|
||||||
|
}
|
||||||
|
$pagination[] = 'gap';
|
||||||
|
} else {
|
||||||
|
if ($page >= 3){
|
||||||
|
for ($i = 2; $i <= $page+2; $i++) {
|
||||||
|
$pagination[] = $i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ($i = 2; $i <= 5; $i++) {
|
||||||
|
$pagination[] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$pagination[] = 'gap';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pages >= 10) {
|
||||||
|
if ($pages == $page)
|
||||||
|
$pagination[] = $i;
|
||||||
|
else
|
||||||
|
$pagination[] = $pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pagination[] = 'nextPage';
|
||||||
|
|
||||||
|
return $pagination;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function link($text, $page, array $attrs = [])
|
||||||
|
{
|
||||||
|
return $this->helper->linkTo($text, array_merge(['#index'], $this->params()->query_parameters(), ['page' => $page]), $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tag($type, $content, array $attrs = [])
|
||||||
|
{
|
||||||
|
return $this->helper->contentTag($type, $content, $attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function params()
|
||||||
|
{
|
||||||
|
return Rails::application()->dispatcher()->parameters();
|
||||||
|
}
|
||||||
|
}
|
52
lib/Rails/ActionView/Helper/WillPaginate.delete/BootstrapRenderer.php
Executable file
52
lib/Rails/ActionView/Helper/WillPaginate.delete/BootstrapRenderer.php
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\WillPaginate;
|
||||||
|
|
||||||
|
class BootstrapRenderer extends AbstractRenderer
|
||||||
|
{
|
||||||
|
public function toHtml()
|
||||||
|
{
|
||||||
|
$html = implode(array_map(function($item){
|
||||||
|
if (is_int($item))
|
||||||
|
return $this->pageNumber($item);
|
||||||
|
else
|
||||||
|
return $this->$item();
|
||||||
|
}, $this->pagination()), $this->options['link_separator']);
|
||||||
|
|
||||||
|
return $this->htmlContainer($this->tag('ul', $html));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pageNumber($page)
|
||||||
|
{
|
||||||
|
if ($page != $this->collection->currentPage())
|
||||||
|
return $this->tag('li', $this->link($page, $page, ['rel' => $this->relValue($page)]));
|
||||||
|
else
|
||||||
|
return $this->tag('li', $this->tag('span', $page), ['class' => 'current']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function gap()
|
||||||
|
{
|
||||||
|
return $this->tag('li', $this->link('…', "#"), ['class' => 'disabled']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function previousPage()
|
||||||
|
{
|
||||||
|
$num = $this->collection->currentPage() > 1 ?
|
||||||
|
$this->collection->currentPage() - 1 : false;
|
||||||
|
return $this->previousOrNextPage($num, $this->options['previous_label'], 'prev');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function nextPage()
|
||||||
|
{
|
||||||
|
$num = $this->collection->currentPage() < $this->collection->totalPages() ?
|
||||||
|
$this->collection->currentPage() + 1 : false;
|
||||||
|
return $this->previousOrNextPage($num, $this->options['next_label'], 'next');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function previousOrNextPage($page, $text, $classname)
|
||||||
|
{
|
||||||
|
if ($page)
|
||||||
|
return $this->tag('li', $this->link($text, $page), ['class' => $classname]);
|
||||||
|
else
|
||||||
|
return $this->tag('li', $this->tag('span', $text), ['class' => $classname . ' disabled']);
|
||||||
|
}
|
||||||
|
}
|
8
lib/Rails/ActionView/Helper/WillPaginate.delete/LegacyRenderer.php
Executable file
8
lib/Rails/ActionView/Helper/WillPaginate.delete/LegacyRenderer.php
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Helper\WillPaginate;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\Collection;
|
||||||
|
|
||||||
|
class LegacyRenderer extends AbstractRenderer
|
||||||
|
{
|
||||||
|
}
|
31
lib/Rails/ActionView/Layout.php
Executable file
31
lib/Rails/ActionView/Layout.php
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
class Layout extends Template
|
||||||
|
{
|
||||||
|
protected $template_filename;
|
||||||
|
|
||||||
|
protected $filename;
|
||||||
|
|
||||||
|
public function __construct($layoutName, array $params = [], $locals = [])
|
||||||
|
{
|
||||||
|
$this->template_filename = $layoutName;
|
||||||
|
|
||||||
|
$this->filename = $this->resolve_layout_file($layoutName);
|
||||||
|
|
||||||
|
if (isset($params['contents'])) {
|
||||||
|
$this->_buffer = $params['contents'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$locals && $this->setLocals($locals);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderContent()
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
require $this->filename;
|
||||||
|
$this->_buffer = ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
17
lib/Rails/ActionView/Partial.php
Executable file
17
lib/Rails/ActionView/Partial.php
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
class Partial extends Template
|
||||||
|
{
|
||||||
|
public function render_content()
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
$this->_init_render();
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function t($name, array $params = [])
|
||||||
|
{
|
||||||
|
return parent::t($name, $params);
|
||||||
|
}
|
||||||
|
}
|
288
lib/Rails/ActionView/Template.php
Executable file
288
lib/Rails/ActionView/Template.php
Executable file
@ -0,0 +1,288 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActionView\Template\Exception;
|
||||||
|
|
||||||
|
class Template extends Base
|
||||||
|
{
|
||||||
|
protected $_filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout filename.
|
||||||
|
*/
|
||||||
|
protected $_layout;
|
||||||
|
|
||||||
|
protected $_layout_name;
|
||||||
|
|
||||||
|
private $_params;
|
||||||
|
|
||||||
|
private $_template_token;
|
||||||
|
|
||||||
|
private $_initial_ob_level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by Inline responder.
|
||||||
|
*/
|
||||||
|
private $_inline_code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To render for first time, render() must be
|
||||||
|
* called, and this will be set to true.
|
||||||
|
* Next times render() is called, it will need
|
||||||
|
* parameters in order to work.
|
||||||
|
*/
|
||||||
|
private $_init_rendered = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template file.
|
||||||
|
*/
|
||||||
|
protected $template_file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full template path.
|
||||||
|
*/
|
||||||
|
protected $template_filename;
|
||||||
|
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
protected $params;
|
||||||
|
|
||||||
|
protected $contents;
|
||||||
|
|
||||||
|
protected $inline_code;
|
||||||
|
|
||||||
|
protected $lambda;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $render_params could be:
|
||||||
|
* - A string, that will be taken as "file".
|
||||||
|
* - An array specifying the type of parameter:
|
||||||
|
* - file: a file that will be required. Can be a relative path (to views folder) or absolute path.
|
||||||
|
* - contents: string that will be included to the layout, if any.
|
||||||
|
* - inline: inline code that will be evaluated.
|
||||||
|
* - lambda: pass an anonyomous function that will be bound to the template and ran.
|
||||||
|
*/
|
||||||
|
public function __construct($render_params, array $params = [], $locals = array())
|
||||||
|
{
|
||||||
|
if (!is_array($render_params)) {
|
||||||
|
$render_params = ['file' => $render_params];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->type = key($render_params);
|
||||||
|
|
||||||
|
switch ($this->type) {
|
||||||
|
case 'file':
|
||||||
|
$this->template_file = array_shift($render_params);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to receive the contents for this template, i.e.
|
||||||
|
* no processing is needed. It will just be added to the layout, if any.
|
||||||
|
*/
|
||||||
|
case 'contents':
|
||||||
|
$this->contents = array_shift($render_params);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contents will be evalued.
|
||||||
|
*/
|
||||||
|
case 'inline':
|
||||||
|
$this->inline_code = array_shift($render_params);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lambda':
|
||||||
|
$this->lambda = array_shift($render_params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$this->params = $params;
|
||||||
|
|
||||||
|
if (isset($params['locals'])) {
|
||||||
|
$locals = $params['locals'];
|
||||||
|
unset($params['locals']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$locals && $this->setLocals($locals);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderContent()
|
||||||
|
{
|
||||||
|
Rails\ActionView\ViewHelpers::load();
|
||||||
|
$this->_set_initial_ob_level();
|
||||||
|
|
||||||
|
if (!$this->_init_rendered) {
|
||||||
|
ob_start();
|
||||||
|
$this->_init_rendered = true;
|
||||||
|
$this->_init_render();
|
||||||
|
$this->_buffer = ob_get_clean();
|
||||||
|
|
||||||
|
if (!$this->_validate_ob_level()) {
|
||||||
|
$status = ob_get_status();
|
||||||
|
throw new Exception\OutputLeakedException(
|
||||||
|
sprintf('Buffer level: %s; File: %s<br />Topmost buffer\'s contents: <br />%s',
|
||||||
|
$status['level'], substr($this->_filename, strlen(Rails::root()) + 1), htmlentities(ob_get_clean()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render(array $params)
|
||||||
|
{
|
||||||
|
$type = key($params);
|
||||||
|
switch ($type) {
|
||||||
|
case 'template':
|
||||||
|
$file = array_shift($params);
|
||||||
|
$template = new self(['file' => $file]);
|
||||||
|
$template->renderContent();
|
||||||
|
return $template->content();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content($name = null)
|
||||||
|
{
|
||||||
|
if (!func_num_args())
|
||||||
|
return $this->_buffer;
|
||||||
|
else
|
||||||
|
return parent::content($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contents()
|
||||||
|
{
|
||||||
|
return $this->content();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finally prints all the view.
|
||||||
|
*/
|
||||||
|
public function get_buffer_and_clean()
|
||||||
|
{
|
||||||
|
$buffer = $this->_buffer;
|
||||||
|
$this->_buffer = null;
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function t($name, array $params = [])
|
||||||
|
{
|
||||||
|
if (is_array($name)) {
|
||||||
|
$params = $name;
|
||||||
|
$name = current($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($name, '.') === 0) {
|
||||||
|
if (is_int(strpos($this->template_filename, Rails::config()->paths->views->toString()))) {
|
||||||
|
$parts = array();
|
||||||
|
$path = substr(
|
||||||
|
$this->template_filename, strlen(Rails::config()->paths->views) + 1,
|
||||||
|
strlen(pathinfo($this->template_filename, PATHINFO_BASENAME)) * -1
|
||||||
|
)
|
||||||
|
. pathinfo($this->template_filename, PATHINFO_FILENAME);
|
||||||
|
|
||||||
|
foreach (explode('/', $path) as $part) {
|
||||||
|
$parts[] = ltrim($part, '_');
|
||||||
|
}
|
||||||
|
$name = implode('.', $parts) . $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::t($name, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _init_render()
|
||||||
|
{
|
||||||
|
$layout_wrap = !empty($this->params['layout']);
|
||||||
|
|
||||||
|
if ($layout_wrap) {
|
||||||
|
$layout_file = $this->resolve_layout_file($this->params['layout']);
|
||||||
|
|
||||||
|
if (!is_file($layout_file))
|
||||||
|
throw new Exception\LayoutMissingException(
|
||||||
|
sprintf("Missing layout '%s' [ file => %s ]",
|
||||||
|
$this->params['layout'], $layout_file)
|
||||||
|
);
|
||||||
|
ob_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->type) {
|
||||||
|
case 'file':
|
||||||
|
$this->build_template_filename();
|
||||||
|
|
||||||
|
if (!is_file($this->template_filename)) {
|
||||||
|
throw new Exception\TemplateMissingException(
|
||||||
|
sprintf("Missing template file %s", $this->template_filename)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
require $this->template_filename;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'contents':
|
||||||
|
echo $this->contents;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'inline':
|
||||||
|
eval('?>' . $this->inline_code . '<?php ');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lambda':
|
||||||
|
$lambda = $this->lambda;
|
||||||
|
$this->lambda = null;
|
||||||
|
$lambda = $lambda->bindTo($this);
|
||||||
|
$lambda();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($layout_wrap) {
|
||||||
|
$this->_buffer = ob_get_clean();
|
||||||
|
$layout = new Layout($layout_file, ['contents' => $this->_buffer], $this->locals);
|
||||||
|
$layout->renderContent();
|
||||||
|
echo $layout->content();
|
||||||
|
// require $layout_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _set_initial_ob_level()
|
||||||
|
{
|
||||||
|
$status = ob_get_status();
|
||||||
|
$this->_initial_ob_level = $this->_get_ob_level();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _validate_ob_level()
|
||||||
|
{
|
||||||
|
$status = ob_get_status();
|
||||||
|
return $this->_initial_ob_level == $this->_get_ob_level();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolve_layout_file($layout)
|
||||||
|
{
|
||||||
|
if (strpos($layout, '/') === 0 || preg_match('/^[A-Za-z]:(?:\\\|\/)/', $layout))
|
||||||
|
return $layout;
|
||||||
|
else {
|
||||||
|
if (is_int(strpos($layout, '.')))
|
||||||
|
return Rails::config()->paths->layouts . '/' . $layout;
|
||||||
|
else
|
||||||
|
return Rails::config()->paths->layouts . '/' . $layout . '.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_template_filename()
|
||||||
|
{
|
||||||
|
$template = $this->template_file;
|
||||||
|
if (strpos($template, '/') === 0 || preg_match('/^[A-Za-z]:(?:\\\|\/)/', $template))
|
||||||
|
$this->template_filename = $template;
|
||||||
|
else {
|
||||||
|
if (is_int(strpos($template, '.'))) {
|
||||||
|
$this->template_filename = Rails::config()->paths->views . '/' . $template;
|
||||||
|
} else {
|
||||||
|
$this->template_filename = Rails::config()->paths->views . '/' . $template . '.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _get_ob_level()
|
||||||
|
{
|
||||||
|
$status = ob_get_status();
|
||||||
|
return !empty($status['level']) ? $status['level'] : 0;
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActionView/Template/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActionView/Template/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Template\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
7
lib/Rails/ActionView/Template/Exception/LayoutMissingException.php
Executable file
7
lib/Rails/ActionView/Template/Exception/LayoutMissingException.php
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Template\Exception;
|
||||||
|
|
||||||
|
class LayoutMissingException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
protected $title = "Layout missing";
|
||||||
|
}
|
6
lib/Rails/ActionView/Template/Exception/OutputLeakedException.php
Executable file
6
lib/Rails/ActionView/Template/Exception/OutputLeakedException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Template\Exception;
|
||||||
|
|
||||||
|
class OutputLeakedException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActionView/Template/Exception/TemplateMissingException.php
Executable file
6
lib/Rails/ActionView/Template/Exception/TemplateMissingException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView\Template\Exception;
|
||||||
|
|
||||||
|
class TemplateMissingException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
109
lib/Rails/ActionView/ViewHelpers.php
Executable file
109
lib/Rails/ActionView/ViewHelpers.php
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
class ViewHelpers
|
||||||
|
{
|
||||||
|
const BASE_HELPER_NAME = 'Rails\ActionView\Helper\Base';
|
||||||
|
|
||||||
|
static protected $registry = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers instances.
|
||||||
|
*/
|
||||||
|
static protected $helpers = [];
|
||||||
|
|
||||||
|
static protected $helpersLoaded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers queue list. They will be available in this order.
|
||||||
|
*/
|
||||||
|
static protected $queue = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the helper that owns $method.
|
||||||
|
*
|
||||||
|
* @return object | false
|
||||||
|
*/
|
||||||
|
static public function findHelperFor($method)
|
||||||
|
{
|
||||||
|
if (isset(self::$registry[$method])) {
|
||||||
|
return self::$helpers[self::$registry[$method]];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (self::$helpers as $helperName => $helper) {
|
||||||
|
if (method_exists($helper, $method)) {
|
||||||
|
self::$registry[$method] = $helperName;
|
||||||
|
return $helper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function getBaseHelper()
|
||||||
|
{
|
||||||
|
return self::getHelper(self::BASE_HELPER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function getHelper($name)
|
||||||
|
{
|
||||||
|
return self::$helpers[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds class names to the helper queues. These classes will be instantiated
|
||||||
|
* in includeHelpers().
|
||||||
|
*
|
||||||
|
* @var name string helper name
|
||||||
|
* @see load()
|
||||||
|
*/
|
||||||
|
static public function addHelper($className)
|
||||||
|
{
|
||||||
|
self::$queue[] = $className;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function addHelpers(array $classNames)
|
||||||
|
{
|
||||||
|
self::$queue = array_merge(self::$queue, $className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For application helpers. Class names passed will be appended with "Helper".
|
||||||
|
*/
|
||||||
|
static public function addAppHelpers(array $helpers)
|
||||||
|
{
|
||||||
|
self::$queue = array_merge(self::$queue, array_map(function($c) { return $c . 'Helper'; }, $helpers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually include the helpers files.
|
||||||
|
*
|
||||||
|
* Application and current controller's helper are added here,
|
||||||
|
* to make sure they're top on the list.
|
||||||
|
*/
|
||||||
|
static public function load()
|
||||||
|
{
|
||||||
|
if (!self::$helpersLoaded) {
|
||||||
|
if (($router = Rails::application()->dispatcher()->router()) && ($route = $router->route())) {
|
||||||
|
$controllerHelper = Rails::services()->get('inflector')->camelize($route->controller()) . 'Helper';
|
||||||
|
array_unshift(self::$queue, $controllerHelper);
|
||||||
|
}
|
||||||
|
$appHelper = 'ApplicationHelper';
|
||||||
|
array_unshift(self::$queue, $appHelper);
|
||||||
|
|
||||||
|
foreach (array_unique(self::$queue) as $name) {
|
||||||
|
try {
|
||||||
|
Rails::loader()->loadClass($name);
|
||||||
|
self::$helpers[$name] = new $name();
|
||||||
|
} catch (Rails\Loader\Exception\ExceptionInterface $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add base helper
|
||||||
|
self::$helpers[self::BASE_HELPER_NAME] = new Helper\Base();
|
||||||
|
|
||||||
|
self::$helpersLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
lib/Rails/ActionView/Xml.php
Executable file
69
lib/Rails/ActionView/Xml.php
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActionView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file could belong somewhere else.
|
||||||
|
*/
|
||||||
|
class Xml
|
||||||
|
{
|
||||||
|
private $_buffer = '';
|
||||||
|
|
||||||
|
public function __call($method, $params)
|
||||||
|
{
|
||||||
|
array_unshift($params, $method);
|
||||||
|
call_user_func_array([$this, 'create'], $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function instruct()
|
||||||
|
{
|
||||||
|
$this->_buffer .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $content could be passed as second argument.
|
||||||
|
*/
|
||||||
|
public function create($root, $attrs, $content = null)
|
||||||
|
{
|
||||||
|
$this->_buffer .= '<'.$root;
|
||||||
|
|
||||||
|
if (!is_array($attrs)) {
|
||||||
|
$content = $attrs;
|
||||||
|
$attrs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attrs) {
|
||||||
|
$attrs_str = [];
|
||||||
|
foreach ($attrs as $name => $val)
|
||||||
|
$attrs_str[] = $name . '="'.htmlspecialchars($val).'"';
|
||||||
|
$this->_buffer .= ' ' . implode(' ', $attrs_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$content) {
|
||||||
|
$this->_buffer .= ' />';
|
||||||
|
} else {
|
||||||
|
$this->_buffer .= ">\n";
|
||||||
|
|
||||||
|
if (is_string($content))
|
||||||
|
$this->_buffer .= $content;
|
||||||
|
elseif ($content instanceof Closure)
|
||||||
|
$this->_buffer .= $content();
|
||||||
|
else
|
||||||
|
throw new Exception\InvalidArgumentError(
|
||||||
|
sprintf('Expecting Closure or string as third argument, %s passed.', gettype($content))
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->_buffer .= '</'.$root.'>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build($el, array $params = [])
|
||||||
|
{
|
||||||
|
$this->_buffer .= (new \Rails\Xml\Xml($el, $params))->output() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function output()
|
||||||
|
{
|
||||||
|
!$this->_buffer && $this->create();
|
||||||
|
return $this->_buffer;
|
||||||
|
}
|
||||||
|
}
|
366
lib/Rails/ActiveModel/Collection.php
Executable file
366
lib/Rails/ActiveModel/Collection.php
Executable file
@ -0,0 +1,366 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveModel;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Rails;
|
||||||
|
|
||||||
|
class Collection implements \ArrayAccess, \Iterator
|
||||||
|
{
|
||||||
|
/* ArrayAccess { */
|
||||||
|
protected $members = array();
|
||||||
|
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
if (is_null($offset)) {
|
||||||
|
$this->members[] = $value;
|
||||||
|
} else {
|
||||||
|
$this->members[$offset] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
return isset($this->members[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
unset($this->members[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return isset($this->members[$offset]) ? $this->members[$offset] : null;
|
||||||
|
}
|
||||||
|
/* } Iterator {*/
|
||||||
|
protected $position = 0;
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
reset($this->members);
|
||||||
|
$this->position = key($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return $this->members[$this->position];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return key($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function next()
|
||||||
|
{
|
||||||
|
next($this->members);
|
||||||
|
$this->position = key($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return array_key_exists($this->position, $this->members);
|
||||||
|
}
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
public function __construct(array $members = array())
|
||||||
|
{
|
||||||
|
$this->members = $members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function merge()
|
||||||
|
{
|
||||||
|
foreach (func_get_args() as $coll) {
|
||||||
|
if ($coll instanceof self)
|
||||||
|
$coll = $coll->members();
|
||||||
|
$this->members = array_merge($this->members, $coll);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function members()
|
||||||
|
{
|
||||||
|
return $this->members;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Another way to get members.
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return $this->members;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each (experimental)
|
||||||
|
*
|
||||||
|
* If string is passed, it'll be taken as method name to be called.
|
||||||
|
* Eg. $posts->each('destroy'); - All posts will be destroyed.
|
||||||
|
* In this case, $params for the method may be passed.
|
||||||
|
*
|
||||||
|
* A Closure may also be passed.
|
||||||
|
*/
|
||||||
|
public function each($function, array $params = array())
|
||||||
|
{
|
||||||
|
if (is_string($function)) {
|
||||||
|
foreach ($this->members() as $m) {
|
||||||
|
call_user_func_array(array($m, $function), $params);
|
||||||
|
}
|
||||||
|
} elseif ($function instanceof Closure) {
|
||||||
|
foreach ($this->members() as $idx => $m) {
|
||||||
|
$function($m, $idx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf('Argument must be an either a string or a Closure, %s passed.', gettype($model))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reduce($var, Closure $block)
|
||||||
|
{
|
||||||
|
foreach ($this->members() as $m) {
|
||||||
|
$var = $block($var, $m);
|
||||||
|
}
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sort(Closure $criteria)
|
||||||
|
{
|
||||||
|
usort($this->members, $criteria);
|
||||||
|
$this->rewind();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unshift($model)
|
||||||
|
{
|
||||||
|
if ($model instanceof Base)
|
||||||
|
$model = array($model);
|
||||||
|
elseif (!$model instanceof self) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf('Argument must be an instance of either ActiveRecord\Base or ActiveRecord\Collection, %s passed.', gettype($model))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($model as $m)
|
||||||
|
array_unshift($this->members, $m);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches objects for a property with a value and returns object.
|
||||||
|
*/
|
||||||
|
public function search($prop, $value)
|
||||||
|
{
|
||||||
|
foreach ($this->members() as $obj) {
|
||||||
|
if ($obj->$prop == $value)
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns a Collection with the models that matches the options.
|
||||||
|
# Eg: $posts->select(array('is_active' => true, 'user_id' => 4));
|
||||||
|
# If Closure passed as $opts, the model that returns == true on the function
|
||||||
|
# will be added.
|
||||||
|
public function select($opts)
|
||||||
|
{
|
||||||
|
$objs = array();
|
||||||
|
|
||||||
|
if (is_array($opts)) {
|
||||||
|
foreach ($this as $obj) {
|
||||||
|
foreach ($opts as $prop => $cond) {
|
||||||
|
if (!$obj->$prop || $obj->$prop != $cond)
|
||||||
|
continue;
|
||||||
|
$objs[] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($opts instanceof Closure) {
|
||||||
|
foreach ($this->members() as $obj) {
|
||||||
|
$opts($obj) && $objs[] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new self($objs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes members according to attributes.
|
||||||
|
*/
|
||||||
|
public function remove($attrs)
|
||||||
|
{
|
||||||
|
!is_array($attrs) && $attrs = array('id' => $attrs);
|
||||||
|
|
||||||
|
foreach ($this->members() as $k => $m) {
|
||||||
|
foreach ($attrs as $a => $v) {
|
||||||
|
if ($m->getAttribute($a) != $v)
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
unset($this->members[$k]);
|
||||||
|
}
|
||||||
|
$this->members = array_values($this->members);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function max($criteria)
|
||||||
|
{
|
||||||
|
if (!$this->members)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$current = key($this->members);
|
||||||
|
if (count($this->members) < 2)
|
||||||
|
return $this->members[$current];
|
||||||
|
|
||||||
|
$max = $this->members[$current];
|
||||||
|
|
||||||
|
if ($criteria instanceof Closure) {
|
||||||
|
$params = $this;
|
||||||
|
foreach($params as $current) {
|
||||||
|
if (!$next = next($params))
|
||||||
|
break;
|
||||||
|
$max = $criteria($max, $next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deprecated in favor of none.
|
||||||
|
public function blank()
|
||||||
|
{
|
||||||
|
return empty($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function none()
|
||||||
|
{
|
||||||
|
return empty($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function any()
|
||||||
|
{
|
||||||
|
return (bool)$this->members;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: xml shouldn't be created here.
|
||||||
|
*/
|
||||||
|
public function toXml()
|
||||||
|
{
|
||||||
|
if ($this->blank())
|
||||||
|
return;
|
||||||
|
|
||||||
|
$t = get_class($this->current());
|
||||||
|
$t = $t::t();
|
||||||
|
|
||||||
|
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
|
||||||
|
$xml .= '<' . $t . '>';
|
||||||
|
|
||||||
|
foreach ($this->members() as $obj) {
|
||||||
|
$xml .= $obj->toXml(array('skip_instruct' => true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$xml .= '</' . $t . '>';
|
||||||
|
|
||||||
|
return $xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function asJson()
|
||||||
|
{
|
||||||
|
$json = [];
|
||||||
|
foreach ($this->members as $member)
|
||||||
|
$json[] = $member->asJson();
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toJson()
|
||||||
|
{
|
||||||
|
return json_encode($this->asJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of the attributes in the models.
|
||||||
|
* $attrs could be a string of a single attribute we want, and
|
||||||
|
* an indexed array will be returned.
|
||||||
|
* If $attrs is an array of many attributes, an associative array will be returned.
|
||||||
|
*
|
||||||
|
* $collection->attributes(['id', 'createdAt']);
|
||||||
|
* No -> $collection->attributes('id', 'name');
|
||||||
|
*/
|
||||||
|
public function getAttributes($attrs)
|
||||||
|
{
|
||||||
|
$models_attrs = array();
|
||||||
|
|
||||||
|
if (is_string($attrs)) {
|
||||||
|
foreach ($this as $m) {
|
||||||
|
// if (Rails::config()->ar2) {
|
||||||
|
// $models_attrs[] = $m->$attrs();
|
||||||
|
// } else {
|
||||||
|
$models_attrs[] = $m->$attrs;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($this->members() as $m) {
|
||||||
|
$model_attrs = [];
|
||||||
|
foreach ($attrs as $attr) {
|
||||||
|
// if (!Rails::config()->ar2) {
|
||||||
|
$model_attrs[$attr] = $m->$attr;
|
||||||
|
// } else {
|
||||||
|
// $model_attrs[$attr] = $m->$attr();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
$models_attrs[] = $model_attrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $models_attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function size()
|
||||||
|
{
|
||||||
|
return count($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removes dupe models based on id or other attribute.
|
||||||
|
public function unique($attr = 'id')
|
||||||
|
{
|
||||||
|
$checked = array();
|
||||||
|
foreach ($this->members() as $k => $obj) {
|
||||||
|
if (in_array($obj->$attr, $checked))
|
||||||
|
unset($this->members[$k]);
|
||||||
|
else
|
||||||
|
$checked[] = $obj->$attr;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
# array_slices the collection.
|
||||||
|
public function slice($offset, $length = null)
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->members = array_slice($clone->members, $offset, $length);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteIf(Closure $conditions)
|
||||||
|
{
|
||||||
|
$deleted = false;
|
||||||
|
foreach ($this->members() as $k => $m) {
|
||||||
|
if ($conditions($m)) {
|
||||||
|
unset($this[$k]);
|
||||||
|
$deleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($deleted)
|
||||||
|
$this->members = array_values($this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function replace($replacement)
|
||||||
|
{
|
||||||
|
if ($replacement instanceof self)
|
||||||
|
$this->members = $replacement->members();
|
||||||
|
elseif (is_array($replacement))
|
||||||
|
$this->members = $replacement;
|
||||||
|
else
|
||||||
|
throw new Exception\InvalidArgumentException(sprintf("%s expects a %s or an array, %s passed", __METHOD__, __CLASS__, gettype($replacement)));
|
||||||
|
}
|
||||||
|
}
|
107
lib/Rails/ActiveModel/Errors.php
Executable file
107
lib/Rails/ActiveModel/Errors.php
Executable file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveModel;
|
||||||
|
|
||||||
|
class Errors
|
||||||
|
{
|
||||||
|
const BASE_ERRORS_INDEX = 'model_base_errors';
|
||||||
|
|
||||||
|
private $errors = array();
|
||||||
|
|
||||||
|
public function add($attribute, $msg = null)
|
||||||
|
{
|
||||||
|
if (!isset($this->errors[$attribute]))
|
||||||
|
$this->errors[$attribute] = array();
|
||||||
|
|
||||||
|
$this->errors[$attribute][] = $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addToBase($msg)
|
||||||
|
{
|
||||||
|
$this->base($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function base($msg)
|
||||||
|
{
|
||||||
|
$this->add(self::BASE_ERRORS_INDEX, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function on($attribute)
|
||||||
|
{
|
||||||
|
if (!isset($this->errors[$attribute]))
|
||||||
|
return null;
|
||||||
|
elseif (count($this->errors[$attribute]) == 1)
|
||||||
|
return current($this->errors[$attribute]);
|
||||||
|
else
|
||||||
|
return $this->errors[$attribute];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onBase()
|
||||||
|
{
|
||||||
|
return $this->on(self::BASE_ERRORS_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
# $glue is a string that, if present, will be used to
|
||||||
|
# return the messages imploded.
|
||||||
|
public function fullMessages($glue = null)
|
||||||
|
{
|
||||||
|
$fullMessages = array();
|
||||||
|
|
||||||
|
foreach ($this->errors as $attr => $errors) {
|
||||||
|
foreach ($errors as $msg) {
|
||||||
|
if ($attr == self::BASE_ERRORS_INDEX)
|
||||||
|
$fullMessages[] = $msg;
|
||||||
|
else
|
||||||
|
$fullMessages[] = $this->_propper_attr($attr) . ' ' . $msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($glue !== null)
|
||||||
|
return implode($glue, $fullMessages);
|
||||||
|
else
|
||||||
|
return $fullMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invalid($attribute)
|
||||||
|
{
|
||||||
|
return isset($this->errors[$attribute]);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deprecated in favor of none().
|
||||||
|
public function blank()
|
||||||
|
{
|
||||||
|
return !(bool)$this->errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function none()
|
||||||
|
{
|
||||||
|
return !(bool)$this->errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function any()
|
||||||
|
{
|
||||||
|
return !$this->blank();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function all()
|
||||||
|
{
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
foreach ($this->errors as $errors) {
|
||||||
|
$i += count($errors);
|
||||||
|
}
|
||||||
|
return $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _propper_attr($attr)
|
||||||
|
{
|
||||||
|
$attr = ucfirst(strtolower($attr));
|
||||||
|
if (is_int(strpos($attr, '_'))) {
|
||||||
|
$attr = str_replace('_', ' ', $attr);
|
||||||
|
}
|
||||||
|
return $attr;
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rails/ActiveModel/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActiveModel/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveModel\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActiveModel/Exception/InvalidArgumentException.php
Executable file
6
lib/Rails/ActiveModel/Exception/InvalidArgumentException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveModel\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \Rails\Exception\InvalidArgumentException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
174
lib/Rails/ActiveRecord/ActiveRecord.php
Executable file
174
lib/Rails/ActiveRecord/ActiveRecord.php
Executable file
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord;
|
||||||
|
|
||||||
|
use PDOStatement;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Connection;
|
||||||
|
|
||||||
|
abstract class ActiveRecord
|
||||||
|
{
|
||||||
|
static private $_prev_connection = '';
|
||||||
|
|
||||||
|
static private
|
||||||
|
$_connection_data = [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different connections.
|
||||||
|
*
|
||||||
|
* $name => ActiveRecord\Connection
|
||||||
|
*/
|
||||||
|
$_connections = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the active connection.
|
||||||
|
*/
|
||||||
|
static private $activeConnectionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an error ocurred when calling execute_sql(),
|
||||||
|
* it will be stored here.
|
||||||
|
*/
|
||||||
|
static private $_last_error;
|
||||||
|
|
||||||
|
static public function setLastError(array $error, $connection_name = null)
|
||||||
|
{
|
||||||
|
self::$_last_error = $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a connection configuration to the list of available connections.
|
||||||
|
*/
|
||||||
|
static public function addConnection($config, $name)
|
||||||
|
{
|
||||||
|
self::$_connection_data[$name] = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default active connection.
|
||||||
|
*/
|
||||||
|
static public function setConnection($name)
|
||||||
|
{
|
||||||
|
self::create_connection($name);
|
||||||
|
self::$activeConnectionName = $name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stablishes a connection from the available connections list.
|
||||||
|
*/
|
||||||
|
static protected function create_connection($name)
|
||||||
|
{
|
||||||
|
# If the connection is already created, return.
|
||||||
|
if (isset(self::$_connections[$name]))
|
||||||
|
return;
|
||||||
|
elseif (self::connectionExists($name))
|
||||||
|
self::$_connections[$name] = new Connection(self::$_connection_data[$name], $name);
|
||||||
|
else
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Connection '%s' does not exist", $name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function connectionExists($name)
|
||||||
|
{
|
||||||
|
return isset(self::$_connection_data[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function set_environment_connection($environment)
|
||||||
|
{
|
||||||
|
if (self::connectionExists($environment))
|
||||||
|
self::setConnection($environment);
|
||||||
|
elseif (self::connectionExists('default'))
|
||||||
|
self::setConnection('default');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns connection. By default, returns the
|
||||||
|
* currenct active one, or the one matching the $name.
|
||||||
|
*
|
||||||
|
* @return ActiveRecord\Connection
|
||||||
|
*/
|
||||||
|
static public function connection($name = null)
|
||||||
|
{
|
||||||
|
if ($name === null) {
|
||||||
|
$name = self::$activeConnectionName;
|
||||||
|
if (!$name)
|
||||||
|
throw new Exception\RuntimeException("No database connection is active");
|
||||||
|
} else {
|
||||||
|
self::create_connection($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$_connections[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns active connection's name.
|
||||||
|
*/
|
||||||
|
static public function activeConnectionName()
|
||||||
|
{
|
||||||
|
return self::$activeConnectionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns active connection's data.
|
||||||
|
*/
|
||||||
|
static public function activeConnectionData()
|
||||||
|
{
|
||||||
|
if (!self::$activeConnectionName)
|
||||||
|
throw new Exception\RuntimeException("No database connection is active");
|
||||||
|
return array_merge(self::$_connection_data[self::$activeConnectionName], ['name' => self::$activeConnectionName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the system when creating schema files.
|
||||||
|
*/
|
||||||
|
static public function connections()
|
||||||
|
{
|
||||||
|
return self::$_connection_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function lastError()
|
||||||
|
{
|
||||||
|
return self::$_last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static private function _include_additional_connection($name)
|
||||||
|
{
|
||||||
|
$config = Rails::application()->config()->active_record;
|
||||||
|
if (!empty($config['additional_connections']) && !empty($config['additional_connections'][$name])) {
|
||||||
|
self::addConnection($config['additional_connections'][$name], $name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This could be somewhere else.
|
||||||
|
*/
|
||||||
|
static public function proper_adapter_name($adapter_name)
|
||||||
|
{
|
||||||
|
$adapter_name = strtolower($adapter_name);
|
||||||
|
|
||||||
|
switch ($adapter_name) {
|
||||||
|
case 'mysql':
|
||||||
|
return 'MySql';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
return 'Sqlite';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return $adapter_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function restore_connection()
|
||||||
|
{
|
||||||
|
if (self::$_prev_connection) {
|
||||||
|
self::setConnection(self::$_prev_connection);
|
||||||
|
self::$_prev_connection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
lib/Rails/ActiveRecord/Adapter/AbstractQueryBuilder.php
Executable file
62
lib/Rails/ActiveRecord/Adapter/AbstractQueryBuilder.php
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\ActiveRecord;
|
||||||
|
use Rails\ActiveRecord\Connection;
|
||||||
|
use Rails\ActiveRecord\Relation;
|
||||||
|
use Rails\ActiveRecord\Relation\AbstractRelation;
|
||||||
|
|
||||||
|
abstract class AbstractQueryBuilder extends Relation
|
||||||
|
{
|
||||||
|
protected
|
||||||
|
$_sql,
|
||||||
|
$_params,
|
||||||
|
$_stmt,
|
||||||
|
$_row_count,
|
||||||
|
$_will_paginate,
|
||||||
|
$complete_sql,
|
||||||
|
$query,
|
||||||
|
$connection;
|
||||||
|
|
||||||
|
public function __construct(Connection $connection)
|
||||||
|
{
|
||||||
|
$this->connection = $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeSql()
|
||||||
|
{
|
||||||
|
$this->_stmt = $this->connection->executeSql($this->_params);
|
||||||
|
if ($this->will_paginate)
|
||||||
|
$this->calculate_found_rows();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function calculate_found_rows();
|
||||||
|
|
||||||
|
abstract protected function _build_sql();
|
||||||
|
|
||||||
|
public function build_sql(AbstractRelation $query)
|
||||||
|
{
|
||||||
|
$complete_sql = $query->complete_sql();
|
||||||
|
$this->query = $query;
|
||||||
|
|
||||||
|
if ($complete_sql) {
|
||||||
|
list ($sql, $params) = $complete_sql;
|
||||||
|
array_unshift($params, $sql);
|
||||||
|
$this->_params = $params;
|
||||||
|
$this->will_paginate = $query->will_paginate();
|
||||||
|
} else {
|
||||||
|
$this->will_paginate = $query->will_paginate();
|
||||||
|
$this->_build_sql();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stmt()
|
||||||
|
{
|
||||||
|
return $this->_stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function row_count()
|
||||||
|
{
|
||||||
|
return $this->_row_count;
|
||||||
|
}
|
||||||
|
}
|
12
lib/Rails/ActiveRecord/Adapter/AbstractTable.php
Executable file
12
lib/Rails/ActiveRecord/Adapter/AbstractTable.php
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use PDOStatement;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\ActiveRecord;
|
||||||
|
use Rails\ActiveRecord\Connection;
|
||||||
|
|
||||||
|
abstract class AbstractTable
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActiveRecord/Adapter/Exception/BadMethodCallException.php
Executable file
6
lib/Rails/ActiveRecord/Adapter/Exception/BadMethodCallException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\Exception;
|
||||||
|
|
||||||
|
class BadMethodCallException extends \Rails\Exception\BadMethodCallException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActiveRecord/Adapter/Exception/ExceptionInterface.php
Executable file
6
lib/Rails/ActiveRecord/Adapter/Exception/ExceptionInterface.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\Exception;
|
||||||
|
|
||||||
|
interface ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
6
lib/Rails/ActiveRecord/Adapter/Exception/RuntimeException.php
Executable file
6
lib/Rails/ActiveRecord/Adapter/Exception/RuntimeException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \Rails\Exception\RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
101
lib/Rails/ActiveRecord/Adapter/MySql/QueryBuilder.php
Executable file
101
lib/Rails/ActiveRecord/Adapter/MySql/QueryBuilder.php
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\MySql;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\Adapter\Exception;
|
||||||
|
use Rails\ActiveRecord\Adapter\AbstractQueryBuilder;
|
||||||
|
|
||||||
|
class QueryBuilder extends AbstractQueryBuilder
|
||||||
|
{
|
||||||
|
protected function calculate_found_rows()
|
||||||
|
{
|
||||||
|
$rows = $this->connection->query("SELECT FOUND_ROWS()");
|
||||||
|
$rows = array_shift($rows);
|
||||||
|
$rows = array_shift($rows);
|
||||||
|
$this->_row_count = (int)$rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _build_sql()
|
||||||
|
{
|
||||||
|
$query = $this->query;
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
$sql = 'SELECT';
|
||||||
|
|
||||||
|
if ($this->will_paginate)
|
||||||
|
$sql .= ' SQL_CALC_FOUND_ROWS';
|
||||||
|
|
||||||
|
if ($query->distinct)
|
||||||
|
$sql .= ' DISTINCT';
|
||||||
|
|
||||||
|
$sql .= ' ' . ($query->select ? implode(', ', $query->select) : '`' . $query->from . '`.*');
|
||||||
|
$sql .= " FROM `" . $query->from . "`";
|
||||||
|
$sql .= ' ' . implode(' ', $query->joins);
|
||||||
|
|
||||||
|
if ($where = $this->_build_where_clause($query)) {
|
||||||
|
$sql .= " WHERE " . $where[0];
|
||||||
|
$params = $where[1];
|
||||||
|
unset($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->group)
|
||||||
|
$sql .= ' GROUP BY ' . implode(', ', $query->group);
|
||||||
|
|
||||||
|
if ($query->order)
|
||||||
|
$sql .= ' ORDER BY ' . implode(', ', $query->order);
|
||||||
|
|
||||||
|
if ($query->having) {
|
||||||
|
$sql .= ' HAVING ' . implode(' ', $query->having);
|
||||||
|
$params = array_merge($params, $query->having_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->offset && $query->limit)
|
||||||
|
$sql .= ' LIMIT ' . $query->offset . ', ' . $query->limit;
|
||||||
|
elseif ($query->limit)
|
||||||
|
$sql .= ' LIMIT ' . $query->limit;
|
||||||
|
|
||||||
|
array_unshift($params, $sql);
|
||||||
|
$this->_params = $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _build_where_clause($query)
|
||||||
|
{
|
||||||
|
if (!$query->where)
|
||||||
|
return;
|
||||||
|
// if (end($query->_where) == 'name in (?)')
|
||||||
|
// vpe($query->where_params);
|
||||||
|
$where = $where_params = [];
|
||||||
|
$param_count = 0;
|
||||||
|
|
||||||
|
foreach ($query->where as $condition) {
|
||||||
|
# Case: ["foo" => $foo, "bar_baz" => $bar];
|
||||||
|
if (is_array($condition)) {
|
||||||
|
foreach ($condition as $column => $value) {
|
||||||
|
$where[] = $column . ' = ?';
|
||||||
|
$where_params[] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($count = substr_count($condition, '?')) {
|
||||||
|
foreach (range(0, $count - 1) as $i) {
|
||||||
|
if (!array_key_exists($param_count, $query->where_params))
|
||||||
|
throw new Exception\RuntimeException(sprintf("Value for question mark placeholder for WHERE clause section wasn't found: %s", $condition));
|
||||||
|
|
||||||
|
if (is_array($query->where_params[$param_count])) {
|
||||||
|
$condition = preg_replace('/\?/', implode(', ', array_fill(0, count($query->where_params[$param_count]), '?')), $condition, 1);
|
||||||
|
$where_params = array_merge($where_params, $query->where_params[$param_count]);
|
||||||
|
} else {
|
||||||
|
$where_params[] = $query->where_params[$param_count];
|
||||||
|
}
|
||||||
|
$param_count++;
|
||||||
|
}
|
||||||
|
} elseif ($count = substr_count($condition, ':')) {
|
||||||
|
$where_params = $query->where_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
$where[] = $condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [implode(' AND ', $where), $where_params];
|
||||||
|
}
|
||||||
|
}
|
55
lib/Rails/ActiveRecord/Adapter/MySql/Table.php
Executable file
55
lib/Rails/ActiveRecord/Adapter/MySql/Table.php
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\MySql;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Connection;
|
||||||
|
use Rails\ActiveRecord\Adapter\AbstractTable;
|
||||||
|
use Rails\ActiveRecord\Exception;
|
||||||
|
|
||||||
|
class Table/* extends AbstractTable*/
|
||||||
|
{
|
||||||
|
static public function fetchSchema(Connection $connection, $table_name)
|
||||||
|
{
|
||||||
|
$stmt = $connection->executeSql("DESCRIBE `".$table_name."`");
|
||||||
|
if (!$rows = $stmt->fetchAll()) {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Couldn't DESCRIBE %s:\n%s", $table_name, var_export($stmt->errorInfo(), true))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_data = $table_indexes = $pri = $uni = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$data = ['type' => $row['Type']];
|
||||||
|
|
||||||
|
if (strpos($row['Type'], 'enum') === 0) {
|
||||||
|
$enum_values = [];
|
||||||
|
foreach (explode(',', substr($row['Type'], 5, -1)) as $opt)
|
||||||
|
$enum_values[] = substr($opt, 1, -1);
|
||||||
|
$data['enum_values'] = $enum_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_data[$row['Field']] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $connection->executeSql("SHOW INDEX FROM `".$table_name."`");
|
||||||
|
$idxs = $stmt->fetchAll();
|
||||||
|
|
||||||
|
if ($idxs) {
|
||||||
|
foreach ($idxs as $idx) {
|
||||||
|
if ($idx['Key_name'] == 'PRIMARY') {
|
||||||
|
$pri[] = $idx['Column_name'];
|
||||||
|
} elseif ($idx['Non_unique'] === '0') {
|
||||||
|
$uni[] = $idx['Column_name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pri)
|
||||||
|
$table_indexes['pri'] = $pri;
|
||||||
|
elseif ($uni)
|
||||||
|
$table_indexes['uni'] = $uni;
|
||||||
|
|
||||||
|
return [$table_data, $table_indexes];
|
||||||
|
}
|
||||||
|
}
|
99
lib/Rails/ActiveRecord/Adapter/Sqlite/QueryBuilder.php
Executable file
99
lib/Rails/ActiveRecord/Adapter/Sqlite/QueryBuilder.php
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\Sqlite;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\Adapter\Exception;
|
||||||
|
use Rails\ActiveRecord\Adapter\AbstractQueryBuilder;
|
||||||
|
|
||||||
|
class QueryBuilder extends AbstractQueryBuilder
|
||||||
|
{
|
||||||
|
public function calculate_found_rows()
|
||||||
|
{
|
||||||
|
$this->query->except('limit')->except('offset')->except('select')->select('COUNT(*) as count_all');
|
||||||
|
$this->_build_sql();
|
||||||
|
|
||||||
|
if ($stmt = $this->connection->executeSql($this->_params)) {
|
||||||
|
$rows = $stmt->fetchAll();
|
||||||
|
if (isset($rows[0]['count_all']))
|
||||||
|
return $rows[0]['count_all'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _build_sql()
|
||||||
|
{
|
||||||
|
$query = $this->query;
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
$sql = 'SELECT';
|
||||||
|
|
||||||
|
if ($query->distinct)
|
||||||
|
$sql .= ' DISTINCT';
|
||||||
|
|
||||||
|
$sql .= ' ' . ($query->select ? implode(', ', $query->select) : '`' . $query->from . '`.*');
|
||||||
|
$sql .= " FROM `" . $query->from . "`";
|
||||||
|
$sql .= ' ' . implode(' ', $query->joins);
|
||||||
|
|
||||||
|
if ($where = $this->_build_where_clause($query)) {
|
||||||
|
$sql .= " WHERE " . $where[0];
|
||||||
|
$params = $where[1];
|
||||||
|
unset($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->group)
|
||||||
|
$sql .= ' GROUP BY ' . implode(', ', $query->group);
|
||||||
|
|
||||||
|
if ($query->order)
|
||||||
|
$sql .= ' ORDER BY ' . implode(', ', $query->order);
|
||||||
|
|
||||||
|
if ($query->having) {
|
||||||
|
$sql .= ' HAVING ' . implode(' ', $query->having);
|
||||||
|
$params = array_merge($params, $query->having_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->offset && $query->limit)
|
||||||
|
$sql .= ' LIMIT ' . $query->offset . ', ' . $query->limit;
|
||||||
|
elseif ($query->limit)
|
||||||
|
$sql .= ' LIMIT ' . $query->limit;
|
||||||
|
|
||||||
|
array_unshift($params, $sql);
|
||||||
|
$this->_params = $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _build_where_clause($query) {
|
||||||
|
if (!$query->where)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$where = $where_params = [];
|
||||||
|
$param_count = 0;
|
||||||
|
|
||||||
|
foreach ($query->where as $condition) {
|
||||||
|
# Case: ["foo" => $foo, "bar" => $bar];
|
||||||
|
if (is_array($condition)) {
|
||||||
|
foreach ($condition as $column => $value) {
|
||||||
|
$where[] = '`' . $column . '` = ?';
|
||||||
|
$where_params[] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($count = substr_count($condition, '?')) {
|
||||||
|
foreach (range(0, $count - 1) as $i) {
|
||||||
|
if (!isset($query->where_params[$param_count]))
|
||||||
|
throw new Exception\RuntimeException(sprintf("Value for question mark placeholder for WHERE clause part wasn't found (%s)", $condition));
|
||||||
|
|
||||||
|
if (is_array($query->where_params[$param_count])) {
|
||||||
|
$condition = preg_replace('/\?/', implode(', ', array_fill(0, count($query->where_params[$param_count]), '?')), $condition, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$where_params[] = $query->where_params[$param_count];
|
||||||
|
$param_count++;
|
||||||
|
}
|
||||||
|
} elseif ($count = substr_count($condition, ':')) {
|
||||||
|
$where_params = $query->where_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
$where[] = $condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [implode(' AND ', $where), $where_params];
|
||||||
|
}
|
||||||
|
}
|
36
lib/Rails/ActiveRecord/Adapter/Sqlite/Table.php
Executable file
36
lib/Rails/ActiveRecord/Adapter/Sqlite/Table.php
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Adapter\Sqlite;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Connection;
|
||||||
|
use Rails\ActiveRecord\Adapter\AbstractTable;
|
||||||
|
|
||||||
|
class Table/* extends AbstractTable*/
|
||||||
|
{
|
||||||
|
static public function fetchSchema(Connection $connection, $table_name)
|
||||||
|
{
|
||||||
|
$stmt = $connection->executeSql("PRAGMA table_info(`".$table_name."`);");
|
||||||
|
|
||||||
|
if (!$rows = $stmt->fetchAll(PDO::FETCH_ASSOC)) {
|
||||||
|
return $stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_data = $pri = $uni = [];
|
||||||
|
|
||||||
|
$table_indexes = [
|
||||||
|
'pri' => [],
|
||||||
|
'uni' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$data = ['type' => $row['type']];
|
||||||
|
$table_data[$row['name']] = $data;
|
||||||
|
|
||||||
|
if ($row['pk'])
|
||||||
|
$table_indexes['pri'][] = $row['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$table_data, $table_indexes];
|
||||||
|
}
|
||||||
|
}
|
1029
lib/Rails/ActiveRecord/Base.php
Executable file
1029
lib/Rails/ActiveRecord/Base.php
Executable file
File diff suppressed because it is too large
Load Diff
1
lib/Rails/ActiveRecord/Base/Methods/AssociationMethods.php
Executable file
1
lib/Rails/ActiveRecord/Base/Methods/AssociationMethods.php
Executable file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
265
lib/Rails/ActiveRecord/Base/Methods/AttributeMethods.php
Executable file
265
lib/Rails/ActiveRecord/Base/Methods/AttributeMethods.php
Executable file
@ -0,0 +1,265 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Base\Methods;
|
||||||
|
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes are properties that correspond to a column in the table.
|
||||||
|
* However, these "properties" are actually stored in the actual instance
|
||||||
|
* property $attributes.
|
||||||
|
*
|
||||||
|
* Models *should* define getters and setters for each attribute, but they
|
||||||
|
* can be called overloadingly (see Rails\ActiveRecord\Base::__call()).
|
||||||
|
* I say *should* because it is said that overloading is bad for performance.
|
||||||
|
*
|
||||||
|
* For convenience (I'd say), in the case of getter methods, the "get" prefix is
|
||||||
|
* omitted (except for methods that require a parameter, like getAttribute($attrName)),
|
||||||
|
* so the expected name of the getter methods is the camel-cased name of the corresponding attribute,
|
||||||
|
* for example createdAt(). This method can either check itself if the index for the attribute exists
|
||||||
|
* in the $attributes array and return it, or simply return getAttribute($attrName).
|
||||||
|
*
|
||||||
|
* Setter methods have the "set" prefix, and they should set the new value in the $attributes array.
|
||||||
|
*/
|
||||||
|
trait AttributeMethods
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Calling attributes throgh magic methods would be like:
|
||||||
|
* $post->createdAt()
|
||||||
|
* The corresponding column for this attribute would be "created_at",
|
||||||
|
* therefore, the attribute name will be converted.
|
||||||
|
* For some cases, to disable the camel to lower conversion,
|
||||||
|
* this property can be set to false.
|
||||||
|
*/
|
||||||
|
static protected $convertAttributeNames = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected to hold only the model's attributes.
|
||||||
|
*/
|
||||||
|
protected $attributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds data grabbed from the database for models
|
||||||
|
* without a primary key, to be able to update them.
|
||||||
|
* Hoever, models should always have a primary key.
|
||||||
|
*/
|
||||||
|
private $storedAttributes = array();
|
||||||
|
|
||||||
|
private $changedAttributes = array();
|
||||||
|
|
||||||
|
static public function convertAttributeNames($value = null)
|
||||||
|
{
|
||||||
|
if (null !== $value) {
|
||||||
|
static::$convertAttributeNames = (bool)$value;
|
||||||
|
} else {
|
||||||
|
return static::$convertAttributeNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function isAttribute($name)
|
||||||
|
{
|
||||||
|
// if (!Rails::config()->ar2) {
|
||||||
|
// return static::table()->columnExists(static::properAttrName($name));
|
||||||
|
// } else {
|
||||||
|
return static::table()->columnExists($name);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method allows to "overloadingly" get attributes this way:
|
||||||
|
* $model->parentId; instead of $model->parent_id.
|
||||||
|
*/
|
||||||
|
static public function properAttrName($name)
|
||||||
|
{
|
||||||
|
if (static::convertAttributeNames()) {
|
||||||
|
$name = \Rails::services()->get('inflector')->underscore($name);
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throw Exception\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function getAttribute($name)
|
||||||
|
{
|
||||||
|
if (array_key_exists($name, $this->attributes)) {
|
||||||
|
return $this->attributes[$name];
|
||||||
|
// } elseif (!Rails::config()->ar2 && static::table()->columnExists(static::properAttrName($name))) {
|
||||||
|
// return null;
|
||||||
|
} elseif (static::table()->columnExists($name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Trying to get non-attribute '%s' from model %s", $name, get_called_class())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAttribute($name, $value)
|
||||||
|
{
|
||||||
|
if (!self::isAttribute($name)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("Trying to set non-attribute '%s' for model %s", $name, get_called_class())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string)$this->getAttribute($name) != (string)$value) {
|
||||||
|
$this->setChangedAttribute($name, $value);
|
||||||
|
}
|
||||||
|
$this->attributes[$name] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function issetAttribute($name)
|
||||||
|
{
|
||||||
|
if (!self::isAttribute($name)) {
|
||||||
|
throw new Exception\InvalidArgumentException(
|
||||||
|
sprintf("'%s' isn't an attribute for model %s", $name, get_called_class())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->attributes[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add/change attributes to model
|
||||||
|
*
|
||||||
|
* Filters protected attributes of the model.
|
||||||
|
* Also calls the "getAttribute()" method, if exists, of the model,
|
||||||
|
* in case extra operation is needed when changing certain attribute.
|
||||||
|
* It's intended to be an equivalent to "def attribute=(val)" in rails.
|
||||||
|
* E.g. "is_held" for post model.
|
||||||
|
*
|
||||||
|
* @see _run_setter()
|
||||||
|
*/
|
||||||
|
public function assignAttributes(array $attrs, array $options = [])
|
||||||
|
{
|
||||||
|
if (!$attrs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($options['without_protection'])) {
|
||||||
|
$this->filterProtectedAttributes($attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!Rails::config()->ar2) {
|
||||||
|
// foreach ($attrs as $attr => $v) {
|
||||||
|
// if ($this->setterExists($attr)) {
|
||||||
|
// $this->_run_setter($attr, $v);
|
||||||
|
// } else {
|
||||||
|
// $this->$attr = $v;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $inflector = Rails::services()->get('inflector');
|
||||||
|
// $reflection = new \ReflectionClass(get_called_class());
|
||||||
|
|
||||||
|
foreach ($attrs as $attrName => $value) {
|
||||||
|
if (self::isAttribute($attrName)) {
|
||||||
|
$this->setAttribute($attrName, $value);
|
||||||
|
} else {
|
||||||
|
if ($setterName = $this->setterExists($attrName)) {
|
||||||
|
$this->$setterName($value);
|
||||||
|
// $setter = 'set' . $inflector->camelize($attrName);
|
||||||
|
} elseif (self::hasPublicProperty($attrName)) {
|
||||||
|
$this->$attrName = $value;
|
||||||
|
// if ($reflection->hasMethod($setter) && $reflection->getMethod($setter)->isPublic()) {
|
||||||
|
// $this->$setter($value);
|
||||||
|
} else {
|
||||||
|
throw new Exception\RuntimeException(
|
||||||
|
sprintf("Can't write unknown attribute '%s' for model %s", $attrName, get_called_class())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attributes()
|
||||||
|
{
|
||||||
|
return $this->attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The changedAttributes array is filled upon updating a record.
|
||||||
|
* When updating, the stored data of the model is retrieved and checked
|
||||||
|
* against the data that will be saved. If an attribute changed, the old value
|
||||||
|
* is stored in this array.
|
||||||
|
*
|
||||||
|
* Calling a method that isn't defined, ending in Changed, for example nameChanged() or
|
||||||
|
* categoryIdChanged(), is the same as calling attributeChanged('name') or
|
||||||
|
* attributeChanged('category_id').
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @see attributeWas()
|
||||||
|
*/
|
||||||
|
public function attributeChanged($attr)
|
||||||
|
{
|
||||||
|
return array_key_exists($attr, $this->changedAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the previous value of an attribute before updating a record. If
|
||||||
|
* it was not changed, returns null.
|
||||||
|
*/
|
||||||
|
public function attributeWas($attr)
|
||||||
|
{
|
||||||
|
return $this->attributeChanged($attr) ? $this->changedAttributes[$attr] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changedAttributes()
|
||||||
|
{
|
||||||
|
return $this->changedAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of the attributes that can't be changed in the model through
|
||||||
|
* assignAttributes().
|
||||||
|
* If both attrAccessible() and attrProtected() are present in the model,
|
||||||
|
* only attrAccessible() will be used.
|
||||||
|
*
|
||||||
|
* Return an empty array so no attributes are protected (except the default ones).
|
||||||
|
*/
|
||||||
|
protected function attrProtected()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of the only attributes that can be changed in the model through
|
||||||
|
* assignAttributes().
|
||||||
|
* If both attrAccessible() and attrProtected() are present in the model,
|
||||||
|
* only attrAccessible() will be used.
|
||||||
|
*
|
||||||
|
* Return an empty array so no attributes are accessible.
|
||||||
|
*/
|
||||||
|
protected function attrAccessible()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setChangedAttribute($attr, $oldValue)
|
||||||
|
{
|
||||||
|
$this->changedAttributes[$attr] = $oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterProtectedAttributes(&$attributes)
|
||||||
|
{
|
||||||
|
# Default protected attributes
|
||||||
|
$default_columns = ['created_at', 'updated_at', 'created_on', 'updated_on'];
|
||||||
|
|
||||||
|
if ($pk = static::table()->primaryKey()) {
|
||||||
|
$default_columns[] = $pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
$default_protected = array_fill_keys(array_merge($default_columns, $this->_associations_names()), true);
|
||||||
|
$attributes = array_diff_key($attributes, $default_protected);
|
||||||
|
|
||||||
|
if (is_array($attrs = $this->attrAccessible())) {
|
||||||
|
$attributes = array_intersect_key($attributes, array_fill_keys($attrs, true));
|
||||||
|
} elseif (is_array($attrs = $this->attrProtected())) {
|
||||||
|
$attributes = array_diff_key($attributes, array_fill_keys($attrs, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
lib/Rails/ActiveRecord/Base/Methods/CounterMethods.php
Executable file
26
lib/Rails/ActiveRecord/Base/Methods/CounterMethods.php
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Base\Methods;
|
||||||
|
|
||||||
|
trait CounterMethods
|
||||||
|
{
|
||||||
|
static public function incrementCounter($counter_name, $id)
|
||||||
|
{
|
||||||
|
return self::updateCounters([$id], [$counter_name => 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function decrementCounter($counter_name, $id)
|
||||||
|
{
|
||||||
|
return self::updateCounters([$id], [$counter_name => -1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function updateCounters(array $ids, array $counters)
|
||||||
|
{
|
||||||
|
if (!is_array($ids))
|
||||||
|
$ids = [$ids];
|
||||||
|
$values = [];
|
||||||
|
foreach ($counters as $name => $value)
|
||||||
|
$values[] = "`" . $name . "` = `".$name."` " . ($value > 0 ? '+' : '-') . " 1";
|
||||||
|
$sql = "UPDATE `".self::tableName()."` SET ".implode(', ', $values)." WHERE id IN (?)";
|
||||||
|
return self::connection()->executeSql($sql, $ids);
|
||||||
|
}
|
||||||
|
}
|
51
lib/Rails/ActiveRecord/Base/Methods/ModelSchemaMethods.php
Executable file
51
lib/Rails/ActiveRecord/Base/Methods/ModelSchemaMethods.php
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Base\Methods;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\ModelSchema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These methods offer information about the table corresponding
|
||||||
|
* to this model, with which the ModelSchema object will be created.
|
||||||
|
*/
|
||||||
|
trait ModelSchemaMethods
|
||||||
|
{
|
||||||
|
static public function table()
|
||||||
|
{
|
||||||
|
$cn = get_called_class();
|
||||||
|
if (!isset(self::$tables[$cn])) {
|
||||||
|
$table = static::initTable();
|
||||||
|
$table->reloadSchema();
|
||||||
|
self::$tables[$cn] = $table;
|
||||||
|
}
|
||||||
|
return self::$tables[$cn];
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function tableName()
|
||||||
|
{
|
||||||
|
$cn = str_replace('\\', '_', get_called_class());
|
||||||
|
$inf = \Rails::services()->get('inflector');
|
||||||
|
|
||||||
|
$tableName = $inf->underscore($inf->pluralize($cn));
|
||||||
|
|
||||||
|
return static::tableNamePrefix() . $tableName . static::tableNameSuffix();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function tableNamePrefix()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function tableNameSuffix()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ModelSchema
|
||||||
|
*/
|
||||||
|
static protected function initTable()
|
||||||
|
{
|
||||||
|
return new ModelSchema(static::tableName(), static::connection());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
240
lib/Rails/ActiveRecord/Base/Methods/RelationMethods.php
Executable file
240
lib/Rails/ActiveRecord/Base/Methods/RelationMethods.php
Executable file
@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Base\Methods;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use Rails;
|
||||||
|
use Rails\ActiveRecord\Relation;
|
||||||
|
use Rails\ActiveRecord\Relation\AbstractRelation;
|
||||||
|
use Rails\ActiveRecord\Relation\Association;
|
||||||
|
|
||||||
|
trait RelationMethods
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Find a model by id.
|
||||||
|
* @return Rails\ActiveRecord\Base
|
||||||
|
* @raises ActiveRecord\RecordNotFound
|
||||||
|
*/
|
||||||
|
static public function find($id)
|
||||||
|
{
|
||||||
|
$id = (int)$id;
|
||||||
|
if ($model = self::where(['id' => $id])->first())
|
||||||
|
return $model;
|
||||||
|
throw new Rails\ActiveRecord\Exception\RecordNotFoundException(
|
||||||
|
sprintf("Couldn't find %s with id = %d.", self::cn(), $id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Instantiator" methods {
|
||||||
|
*/
|
||||||
|
static public function from()
|
||||||
|
{
|
||||||
|
return self::createRelation('select', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function group()
|
||||||
|
{
|
||||||
|
return self::createRelation('select', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function having()
|
||||||
|
{
|
||||||
|
return self::createRelation('having', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function joins()
|
||||||
|
{
|
||||||
|
return self::createRelation('joins', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function limit()
|
||||||
|
{
|
||||||
|
return self::createRelation('limit', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
static public function unscoped()
|
||||||
|
{
|
||||||
|
return new Relation(get_called_class(), static::tableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the correct method to instantiate an empty relation.
|
||||||
|
*/
|
||||||
|
static public function none()
|
||||||
|
{
|
||||||
|
// $cn = self::cn();
|
||||||
|
// return new Relation($cn, static::tableName());
|
||||||
|
return self::createRelation();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function offset()
|
||||||
|
{
|
||||||
|
return self::createRelation('offset', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function order()
|
||||||
|
{
|
||||||
|
return self::createRelation('order', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function select()
|
||||||
|
{
|
||||||
|
return self::createRelation('select', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function distinct()
|
||||||
|
{
|
||||||
|
return self::createRelation('distinct', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function where()
|
||||||
|
{
|
||||||
|
return self::createRelation('where', func_get_args());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take all records.
|
||||||
|
*/
|
||||||
|
static public function all()
|
||||||
|
{
|
||||||
|
return self::none()->take();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For directly pagination without conditions.
|
||||||
|
*/
|
||||||
|
static public function paginate($page, $per_page)
|
||||||
|
{
|
||||||
|
$query = self::createRelation();
|
||||||
|
return $query->paginate($page, $per_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ActiveRecord\Collection
|
||||||
|
*/
|
||||||
|
static public function createModelsFromQuery(AbstractRelation $query)
|
||||||
|
{
|
||||||
|
if ($query->will_paginate()) {
|
||||||
|
$params = [
|
||||||
|
'page' => $query->get_page(),
|
||||||
|
'perPage' => $query->get_per_page(),
|
||||||
|
'offset' => $query->get_offset(),
|
||||||
|
'totalRows' => $query->get_row_count()
|
||||||
|
];
|
||||||
|
} else
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
return self::_create_collection($query->get_results()->fetchAll(PDO::FETCH_ASSOC), $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static protected function createRelation($init_method = null, array $init_args = [])
|
||||||
|
{
|
||||||
|
$cn = self::cn();
|
||||||
|
$query = new Relation($cn, static::tableName());
|
||||||
|
if ($init_method)
|
||||||
|
call_user_func_array([$query, $init_method], $init_args);
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When calling this method for pagination, how do we tell it
|
||||||
|
* the values for page and per_page? That's what extra_params is for...
|
||||||
|
* Although maybe it's not the most elegant solution.
|
||||||
|
* extra_params accepts 'page' and 'per_page', they will be sent to Query
|
||||||
|
* where they will be parsed.
|
||||||
|
*/
|
||||||
|
static public function findBySql($sql, array $params = array(), array $extra_params = array())
|
||||||
|
{
|
||||||
|
$query = self::createRelation();
|
||||||
|
|
||||||
|
$query->complete_sql($sql, $params, $extra_params);
|
||||||
|
|
||||||
|
return $query->take();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _find_has_one($prop, array $params)
|
||||||
|
{
|
||||||
|
empty($params['class_name']) && $params['class_name'] = Rails::services()->get('inflector')->camelize($prop);
|
||||||
|
|
||||||
|
$builder = new Association($params, $this);
|
||||||
|
$builder->build_query();
|
||||||
|
return $builder->get_query()->first() ?: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $params - Additional parameters to customize the query for the association
|
||||||
|
*/
|
||||||
|
private function _find_has_many($prop, $params)
|
||||||
|
{
|
||||||
|
empty($params['class_name']) && $params['class_name'] = rtrim(ucfirst($prop), 's');
|
||||||
|
|
||||||
|
$builder = new Association($params, $this);
|
||||||
|
$builder->build_query();
|
||||||
|
|
||||||
|
return $builder->get_query()->take() ?: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _find_belongs_to($prop, array $params)
|
||||||
|
{
|
||||||
|
empty($params['class_name']) && $params['class_name'] = ucfirst($prop);
|
||||||
|
|
||||||
|
$foreign_key = !empty($params['foreign_key']) ? $params['foreign_key'] : Rails::services()->get('inflector')->underscore($prop) . '_id';
|
||||||
|
|
||||||
|
if ($this->getAttribute($foreign_key)) {
|
||||||
|
return $params['class_name']::where(['id' => $this->getAttribute($foreign_key)])->first() ?: false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _find_has_and_belongs_to_many($prop, array $params)
|
||||||
|
{
|
||||||
|
$find_params = [];
|
||||||
|
|
||||||
|
empty($params['class_name']) && $params['class_name'] = ucfirst(Rails::services()->get('inflector')->singularize($prop));
|
||||||
|
|
||||||
|
$find_params['class_name'] = $params['class_name'];
|
||||||
|
$find_params['from'] = $find_params['class_name']::tableName();
|
||||||
|
|
||||||
|
empty($find_params['join_table']) && $find_params['join_table'] = $find_params['class_name']::tableName() . '_' . $find_params['from'];
|
||||||
|
|
||||||
|
empty($find_params['join_type']) && $find_params['join_type'] = 'join';
|
||||||
|
empty($find_params['join_table_key']) && $find_params['join_table_key'] = 'id';
|
||||||
|
empty($find_params['association_foreign_key']) && $find_params['association_foreign_key'] = substr($find_params['from'], 0, -1) . '_id';
|
||||||
|
|
||||||
|
$joins = strtoupper($find_params['join_type']) . ' `' . $find_params['join_table'] . '` ON `' . $find_params['from'] . '`.`' . $find_params['join_table_key'] . '` = `' . $find_params['join_table'] . '`.`' . $find_params['association_foreign_key'] . '`';
|
||||||
|
|
||||||
|
## Having Post model with has_and_belongs_to_many => tags (Tag model):
|
||||||
|
// 'join_table' => posts_tags [not this => (or tags_posts)]
|
||||||
|
// 'join_table_key' => id
|
||||||
|
// 'foreign_key' => post_id
|
||||||
|
// 'association_foreign_key' => tag_id
|
||||||
|
|
||||||
|
# Needed SQL params
|
||||||
|
// 'select' => posts.*
|
||||||
|
// 'joins' => JOIN posts_tags ON posts.id = posts_tags.post_id',
|
||||||
|
// 'conditions' => array('posts_tags.post_id = ?', $this->id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
'has_and_belongs_to_many' => [
|
||||||
|
'tags' => [
|
||||||
|
'join_type' => 'join',
|
||||||
|
'join_table_key' => 'id',
|
||||||
|
'foreign_key' => 'post_id',
|
||||||
|
'association_foreign_key' => 'tag_id',
|
||||||
|
'select' => 'posts.*'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
*/
|
||||||
|
$relation = new Association($find_params, $this);
|
||||||
|
$relation->build_query();
|
||||||
|
$relation->get_query()->joins($joins);
|
||||||
|
return $relation->get_query()->take() ?: false;
|
||||||
|
}
|
||||||
|
}
|
63
lib/Rails/ActiveRecord/Base/Methods/ScopingMethods.php
Executable file
63
lib/Rails/ActiveRecord/Base/Methods/ScopingMethods.php
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace Rails\ActiveRecord\Base\Methods;
|
||||||
|
|
||||||
|
use Rails\ActiveRecord\Relation;
|
||||||
|
|
||||||
|
trait ScopingMethods
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Checks if scope exists. If it does, executes it and
|
||||||
|
* returns the relation; otherwise, returns false.
|
||||||
|
*/
|
||||||
|
static public function scope($name, array $params)
|
||||||
|
{
|
||||||
|
$cn = self::cn();
|
||||||
|
self::$preventInit = true;
|
||||||
|
$scopes = (new $cn())->scopes();
|
||||||
|
|
||||||
|
if (isset($scopes[$name])) {
|
||||||
|
$lambda = $scopes[$name];
|
||||||
|
$relation = new Relation($cn, static::tableName());
|
||||||
|
|
||||||
|
$lambda = $lambda->bindTo($relation);
|
||||||
|
call_user_func_array($lambda, $params);
|
||||||
|
return $relation;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines scopes.
|
||||||
|
*
|
||||||
|
* Eg:
|
||||||
|
*
|
||||||
|
class Book extends ActiveRecord\Base
|
||||||
|
{
|
||||||
|
static protected function scopes()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'fantasy' => function() {
|
||||||
|
$this->where(['genre' => 'fantasy']);
|
||||||
|
},
|
||||||
|
'available' => function() {
|
||||||
|
$this->where(['status' => 'available']);
|
||||||
|
},
|
||||||
|
'author' => function($author_name, $limit = 0) {
|
||||||
|
$this->where('author_name = ?', $author_name);
|
||||||
|
if ($limit)
|
||||||
|
$this->limit($limit);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
* The anonymous function is binded to an ActiveRecord\Relation object.
|
||||||
|
*
|
||||||
|
* Not supporting default scope for now.
|
||||||
|
* Not supporting static methods as scopes, as they couldn't
|
||||||
|
* be called from a model instance.
|
||||||
|
*/
|
||||||
|
protected function scopes()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user