ZF1: Routes in database - php

I'm building a simple CMS module for my ZendFramework application.
Currently I have all my routes in a .ini file, would it be possible to make it db-driven. Or if it's possible to build a fallback method to a DB-check if the route doesn't exists in the .ini file.
UPDATE - SOLUTION
Hope this will help somebody - Please note that I have custom My_Db_table and My_Db_Row, which I haven't posted here. So ->fetchAllActive() probably needs change to ->fetchAll(); and the getters on the row object may/may-not work, can't remember if they are custom - you'll figure it out ;-)
But Router basically check against DB if none of the other routes apply, i.e. from a .ini file. I haven't checked if this works with all the route types, but it works with the default route type. I use DB routes to point urls down to a pageController with additional params like pageId stored as a JSON string in the route_defaults cell. But you can basically use this for all kind of routes.
In bootstrap
function _initApplication ()
{
// Something taken out for simplicity
$this->bootstrap('frontcontroller');
$front = $this->getResource('frontcontroller');
$router = new My_Controller_Router_Rewrite();
$front->setRouter($router);
// Something taken out for simplicity
}
My/Controller/Router/Rewrite.php
<?php
class My_Controller_Router_Rewrite extends Zend_Controller_Router_Rewrite
{
/**
* Retrieve a named route
*
* #param string $name Name of the route
* #throws Zend_Controller_Router_Exception
* #return Zend_Controller_Router_Route_Interface Route object
*/
public function getRoute($name)
{
if (!isset($this->_routes[$name])) {
/* BEGIN - DB routes */
$routes = new Routes();
$route = $routes->getNamedRoute($name);
if($route instanceof Zend_Controller_Router_Route_Abstract) {
$this->addRoute($name, $route);
}
/* END - DB routes */
if (!isset($this->_routes[$name])) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception("Route $name is not defined");
}
}
return $this->_routes[$name];
}
/**
* Find a matching route to the current PATH_INFO and inject
* returning values to the Request object.
*
* #throws Zend_Controller_Router_Exception
* #return Zend_Controller_Request_Abstract Request object
*/
public function route(Zend_Controller_Request_Abstract $request)
{
if (!$request instanceof Zend_Controller_Request_Http) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object');
}
if ($this->_useDefaultRoutes) {
$this->addDefaultRoutes();
}
// Find the matching route
$routeMatched = false;
foreach (array_reverse($this->_routes, true) as $name => $route) {
// TODO: Should be an interface method. Hack for 1.0 BC
if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
continue;
}
// TODO: Should be an interface method. Hack for 1.0 BC
if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
$match = $request->getPathInfo();
} else {
$match = $request;
}
if ($params = $route->match($match)) {
$this->_setRequestParams($request, $params);
$this->_currentRoute = $name;
$routeMatched = true;
break;
}
}
/* BEGIN - DB routes */
$front = Zend_Controller_Front::getInstance();
if (!$routeMatched || ($routeMatched && !Zend_Controller_Front::getInstance()->getDispatcher()->isDispatchable($request))) {
$routes = new Routes();
$dbRoutes = $routes->getRouterRoutes();
foreach ($dbRoutes as $name => $route) {
// TODO: Should be an interface method. Hack for 1.0 BC
if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
continue;
}
// TODO: Should be an interface method. Hack for 1.0 BC
if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
$match = $request->getPathInfo();
} else {
$match = $request;
}
if ($params = $route->match($match)) {
$this->_setRequestParams($request, $params);
$this->_currentRoute = $name;
$routeMatched = true;
break;
}
}
}
/* END - DB routes */
if(!$routeMatched) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('No route matched the request', 404);
}
if($this->_useCurrentParamsAsGlobal) {
$params = $request->getParams();
foreach($params as $param => $value) {
$this->setGlobalParam($param, $value);
}
}
return $request;
}
}
Routes Db_Table model
<?php
class Routes extends My_Db_Table
{
protected $_name = 'routes';
protected $_rowClass = 'Route';
public static $primaryColumn = 'route_id';
public static $statusColumn = 'route_status';
public static $nameColumn = 'route_name';
public static $typeColumn = 'route_type';
public static $urlColumn = 'route_url';
public static $moduleColumn = 'route_module';
public static $controllerColumnn = 'route_controller';
public static $actionColumnn = 'route_action';
public static $defaultsColumnn = 'route_defaults';
public static $reqsColumnn = 'route_reqs';
public static $createdColumnn = 'route_created';
public function getRouterRoutes() {
$routes = array();
$rowset = $this->fetchAllActive();
foreach($rowset as $row) {
$routes[$row->getName()] = $row->getRouteObject();
}
return $routes;
}
public function getNamedRoute($name) {
$select = $this->select()
->where(self::$statusColumn . ' = ?', 1)
->where(self::$nameColumn . ' = ?', $name);
$rowset = $this->fetchAll($select);
foreach($rowset as $row) {
return $row->getRouteObject();
}
}
}
Route - Db_Table_row
<?php
class Route extends My_Db_Table_Row_Observable
{
public function getType() {
if(empty($this->{Routes::$typeColumn})) {
return "Zend_Controller_Router_Route";
} else {
return $this->{Routes::$typeColumn};
}
}
public function getDefaults() {
$defaults = $this->{Routes::$defaultsColumnn};
if($defaults) {
$defaults = Zend_Json::decode($defaults);
} else {
$defaults = array();
}
$defaults['module'] = $this->getModule();
$defaults['controller'] = $this->getController();
$defaults['action'] = $this->getAction();
return $defaults;
}
public function getReqs() {
$reqs = $this->{Routes::$reqsColumnn};
if($reqs) {
$reqs = Zend_Json::decode($reqs);
} else {
$reqs = array();
}
return $reqs;
}
public function getModule() {
if(empty($this->{Routes::$moduleColumn})) {
return "default";
} else {
return $this->{Routes::$moduleColumn};
}
}
public function getController() {
if(empty($this->{Routes::$controllerColumnn})) {
return "default";
} else {
return $this->{Routes::$controllerColumnn};
}
}
public function getAction() {
if(empty($this->{Routes::$actionColumnn})) {
return "default";
} else {
return $this->{Routes::$actionColumnn};
}
}
public function getRouteObject() {
$class = $this->getType();
$defaults = $this->getDefaults();
$reqs = $this->getReqs();
$route = new $class($this->getUrl(), $defaults, $reqs);
return $route;
}
}
SQL schema for routes table
CREATE TABLE IF NOT EXISTS `routes` (
`route_id` smallint(1) unsigned NOT NULL AUTO_INCREMENT,
`route_status` tinyint(1) unsigned NOT NULL,
`route_name` varchar(16) NOT NULL,
`route_type` varchar(255) NOT NULL,
`route_url` varchar(255) NOT NULL,
`route_module` varchar(32) NOT NULL,
`route_controller` varchar(32) NOT NULL,
`route_action` varchar(32) NOT NULL,
`route_defaults` varchar(255) NOT NULL,
`route_reqs` varchar(255) NOT NULL,
`route_created` datetime NOT NULL,
PRIMARY KEY (`route_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

There are three approaches that I can think of.
First is what I've successfully used in ZF1. Here's the code for the routing itself. The idea is simple: set an "alias" routing (which, in my case, simply used the ".html" suffix to differentiate it from other URLs). If the routing is found, you can get the alias from the DB and then forward the request from the target controller+action to what's defined in the DB (like here). My code isn't pretty, but it works.
Second: write your own router. You can extend the router class and simply add yout own route resolving rules there: get data from the DB, return true (and set params) if the alias is in the database.
Third: scrape the aliases table and store everything in an .ini file (or whatever cache you might be using). This is not much different from what you've implemented already and all you'd need to do would be to automate the alias scraping.

Related

Dirty Methods in Rails equivalent in PHP Codeignitor

I am new to PHP & Codeigniter but it was needed some kind of implementation in PHP.
Following are dirty methods are provided in rails framework by default, here person is model object representing row inside persons table.
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_changed?(from: nil, to: "Bob") # => true
person.name_was # => nil
person.name_change # => [nil, "Bob"]
person.name = 'Bill'
person.name_change # => [nil, "Bill"]
I am interested in to & from specially, Please suggest whether it is possible with any way.
If you would consider Laravel's elquent framework you have a great deal of that functionality already.
Laravel Eloquent update just if changes have been made
It holds an array of the "original" values in the Model, and if any of them have been changed it will commit them to the database.
They also come with a lot of events you can plug into(beforeSave, afterSave, beforeCreate, afterCreate, validation rules, etc...) and they can be extended easily. It might be the closest compatible thing I can imagine you're looking for.
This is however not codeigniter, it relies on a different framework. If you're not dead set on codeigniter you might consider switching to a framework like Laravel or OctoberCMS depending on your needs.
Edit because you're stuck with codeigniter
You might wish to use a library like this one: https://github.com/yidas/codeigniter-model
It is then very easy to extend with some custom caching mechanisms.
The code below is something you could use as a basis for your own model implementation.
It has a very rudementary logic basis, but allows you to check on the dirty status and roll back any changes made to the model.
Note this this is very rudementary, and might even contain a few errors because I have not run this code. it's more a proof of concept to help you create a model that suits your needs.
class User extends CI_Model
{
public $table = 'users';
public $primaryKey = 'id';
public $attributes;
public $original;
public $dirty = [];
public $exists = false;
function __construct()
{
parent::Model();
}
public static function find($model_id)
{
$static = new static;
$query = $static->db->query("SELECT * FROM ' . $static->getTable() . ' WHERE ' . $this->getKeyName() . ' = ?", [$model_id]);
if($result->num_rows()) {
$static->fill($query->row_array());
$static->exists = true;
}
else {
return null;
}
return $static;
}
public function getKeyName()
{
return $this->primaryKey;
}
public function getKey()
{
return $this->getAttribute($this->getKeyName());
}
public function getTable()
{
return $this->table;
}
public function fill($attributes)
{
if(is_null($this->original)) {
$this->original = $attributes;
$this->dirty = $attributes;
}
else {
foreach($attributes as $key => $value) {
if($this->original[$key] != $value) {
$this->dirty[$key] = $value;
}
}
}
$this->attributes = $attributes;
}
public function reset()
{
$this->dirty = [];
$this->attributes = $this->original;
}
public function getAttribute($attribute_name)
{
return array_key_exists($attribute_name, $this->attributes) ? $this->attributes[$attribute_name] : null;
}
public function __get($key)
{
return $this->getAttribute($key);
}
public function __set($key, $value)
{
$this->setAttribute($key, $value);
}
public function setAttribute($key, $value)
{
if(array_key_exists($key, $this->original)) {
if($this->original[$key] !== $value) {
$this->dirty[$key] = $value;
}
}
else {
$this->original[$key] = $value;
$this->dirty[$key] = $value;
}
$this->attributes[$key] = $value;
}
public function getDirty()
{
return $this->dirty;
}
public function isDirty()
{
return (bool)count($this->dirty);
}
public function save()
{
if($this->isDirty()) {
if($this->exists)
{
$this->db->where($this->getKeyName(), $this->getKey());
$this->db->update($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
}
else
{
$this->db->insert($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
$this->attributes[$this->getKeyName()] = $this->db->insert_id();
$this->original[$this->getKeyName()] = $this->getKey();
$this->exists = true;
}
}
}
}
if($user = User::find(1)) {
$user->name = "Johnny Bravo";
$user->save();
}

Architecture Design Help - OOP Solid Principle

I'm trying to make open source Cache library. The purpose of library is to provide the way of storing the variable (can be object, can be be array, can be anything) to files and then retrieved it back on call. (usually those variable value is result of massive database queries and calculations).
The basic aim of the project is to practice the Object Oriented Design Principle Called Solid.
If any one can indicate where i'm violating solid principle and how to fix it
I totally understand stackoverflow is not a code writing service but hey i'm making this library open source, so it would benefit our community.
So here is my file structure.
I'm new to UML so please ignore if any errors found
here is the classes implementation.
Cache
namespace library\pingle\cache;
use library\pingle\cache\config\CacheConfigurator;
use library\pingle\cache\file\FileHandler;
/**
* #property CacheReader $cache_reader
* #property CacheWriter $cache_write
*/
Class Cache {
private $config;
private $file_hander;
private $cache_reader;
private $cache_write;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
private $file_path;
function __construct(CacheConfigurator $config) {
$this->file_hander = new FileHandler();
$this->config = $config;
list($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams) = $this->config->getConfig();
$this->file_path = $this->generateFileName($this->cache_file_prams);
}
public function read() {
if (is_null($this->cache_reader)) {
$this->cache_reader = new CacheReader($this->file_hander);
}
return $this->cache_reader->readCache($this->file_path);
}
public function write($data) {
if (is_null($this->cache_write)) {
$this->cache_write = new CacheWriter($this->file_hander);
}
if (!$this->file_hander->checkDirectory($this->cache_directory . "/" . $this->function_name)) {
$this->file_hander->createDirectory($this->cache_directory . "/" . $this->function_name);
}
$this->cache_write->writeCache($this->file_path, $data);
}
public function check() {
if ($this->file_hander->checkFileExits($this->file_path)) {
if (time() - filemtime($this->file_path) >= 60 * 60 * 24 * $this->cache_kept_days) {
return false;
}
return true;
} else {
return false;
}
}
private function generateFileName(Array $nameprams) {
$this->file_name = "";
$file = "CC";
foreach ($nameprams as $key => $value) {
$file .= "-$key|$value-";
}
$file .= ".bak";
return $this->cache_directory . "/" . $this->function_name . "/" . $file;
}
}
AbstractCache
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
abstract Class AbstractCache {
protected $file_handler;
public function __construct(FileHandler $file_handler) {
$this->file_handler = $file_handler;
}
protected function checkDirectory($path) {
//check directory exists
$dircheck = $this->file_handler->checkDirectory(dirname($path));
if ($dircheck) {
//check directory permission
if ($this->file_handler->checkPermission(dirname($path))) {
return true;
} else {
throw new \Exception("Directory ($path) Permission Error.");
}
} else {
throw new \Exception("Directory ($path) not found.");
}
}
}
CacheReader
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* #property FileHandler $file_handler
*/
Class CacheReader extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
public function readCache($path) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
return $this->file_handler->readFile($path);
} else {
throw new \Exception("File ($path) not found");
}
}
}
}
CacheWriter
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* #property FileHandler $file_handler
*/
Class CacheWriter extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
function writeCache($path, $data) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
$this->file_handler->deleteFile($path);
}
//write cache
$this->file_handler->writeFile($path, $data);
}
}
}
FileHandler
<?php
namespace library\pingle\cache\file;
Class FileHandler {
public function writeFile($path, $data) {
$content = serialize($data);
file_put_contents($path, $content);
}
public function createDirectory($path) {
mkdir($path);
}
public function deleteFile($path) {
unlink($path);
}
public function checkDirectory($path) {
if (file_exists($path)) {
return true;
} else {
return false;
}
}
public function checkPermission($path) {
if (is_writable($path)) {
return true;
} else {
return false;
}
}
public function checkFileExits($path) {
if (is_file($path)) {
return true;
}
return false;
}
public function readFile($path) {
return unserialize(file_get_contents($path));
}
public function checkFileCreated($path, $format = "Y-m-d") {
return date($format, filemtime($path));
}
}
CacheConfigurator
<?php
namespace library\pingle\cache\config;
/**
* #property PramsFormatter $prams_formatter
*/
class CacheConfigurator {
private $prams_formatter;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
function __construct($file_prams) {
$this->cache_file_prams = $file_prams;
$this->cache_directory = ""; //Def Path
}
public function setCacheDirectory($cache_directory) {
$this->cache_directory = $cache_directory;
return $this;
}
public function setFunction($function) {
$this->function_name = $function;
return $this;
}
public function setCacheKeptDays($cache_kept_days) {
$this->cache_kept_days = $cache_kept_days;
return $this;
}
public function getConfig() {
$this->prams_formatter = new PramsFormatter($this->cache_file_prams);
$this->cache_file_prams = $this->prams_formatter->getFormattedPrams();
$this->function_name = $this->prams_formatter->cleanValue($this->function_name);
return array($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams);
}
}
PramsFormatter
<?php
namespace library\pingle\cache\config;
class PramsFormatter {
private $cache_file_prams;
public function __construct(Array $prams) {
$this->cache_file_prams = $prams;
$this->formatPrams();
}
public function formatPrams() {
if (is_array($this->cache_file_prams)) {
foreach ($this->cache_file_prams as $k => $value) {
$this->cache_file_prams[$k] = $this->cleanValue($value);
}
}
}
public function cleanValue($value) {
if (is_array($value)) {
throw new \Exception("Array as paramter value is not accepted");
} else {
return str_replace(array(" ", " ", ".", "/", "\\"), "-", $value);
}
}
public function getFormattedPrams() {
return $this->cache_file_prams;
}
}
Usage
$cache_config = new CacheConfigurator(array('carrier_id' => $invoicedata['carrier_id'], 'month' => $month, 'year' => $year));
$cache_config->setFunction('Inter-department Calls');
$cache_config->setCacheKeptDays(30);
$cache_config->setCacheDirectory("bin/cache");
$cache = new Cache($cache_config);
if ($cache->check()) {
$node = $cache->read();
} else {
//calculate node
$cache->write($node);
}
Git Repository with Improve Design
https://github.com/FaizRasool/EPC
Very good question, but an entire book could probably be written on this, which makes it quite hard to answer.
I'd start with this simple question: what better describes caching in the choices below?
Caching is a mechanism that allows to store the result of a function to a file for a number of days in order to provide fast access to it.
Caching is a mechanism that allows to retain the result of an operation for as long as the associated retention policy is satisfied in order to provide fast access to it.
None of the definitions are perfect and that's not the point, but what I wanted to emphasis is that #1 explains caching with very specific infrastructure details while #2 defines the mechanism in a more abstract way.
Hopefully, you will now have realized one of the biggest flaws of your design IMO. The various abstractions are wrong:
The abstraction of the storage mechanism is dependent of a specific infrastructure detail: the entire API revolves around files. What if I wanted an in-memory cache?
The data retention policy algorithm is very specific: data will be retained only for a specific number of days. What if I wanted to express the cache in minutes where the counter resets evertime the data is accessed?
One advice I'd give you is to always challenge your abstractions and make sure that they are not too specific so that your code can be extensible and reusable, but not too wide either. Paying attention to the language of the problem domain greatly helps with that.
There's obviously much more than this that could be said and sometimes being technology-dependent is the right choice, but I think my answer will help...

Creating customer specific routes in Zend Framework 2

I'm developing a WebApp which (as usual) must support customer specific functionalities.
To achieve it I plan to set the customer name in the local app configuration (config/autoload/local.php )
configuration file so that I can use it to call the specialized code later on.
The module folder structure is this:
/module/Application
/module/Application/config
/module/Application/src
/module/Application/src/Application
/module/Application/src/Application/Controller
/module/Application/src/Application/Controller/[customer_instance_name]
/module/Application/src/Application/Model
/module/Application/src/Application/Model/[customer_instance_name]
/module/Application/view
/module/Application/view/Application
/module/Application/view/Application/[action]
/module/Application/view/Application/[action]/[customer_instance_name]
Using a custom ViewModel I inject the specific customer name to the template path:
namespace Application\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver\TemplatePathStack;
use Zend\Mvc\Service\ViewTemplatePathStackFactory;
class MyViewModel extends ViewModel
{
private $customInstanceName;
private $pathStack;
/**
* Constructor
*
* #param null|array|Traversable $variables
* #param array|Traversable $options
*/
public function __construct($variables = null, $options = null)
{
parent::__construct ( $variables, $options );
$serviceLocator = MySingleton::instance()->serviceLocator;
$factory = new ViewTemplatePathStackFactory();
$this->pathStack = $factory->createService($serviceLocator);
$config = $serviceLocator->get('config');
if (isset($config['custom_instance_name']) AND ($config['custom_instance_name']!='')) {
$this->customInstanceName = $config['custom_instance_name'];
} else {
$this->customInstanceName = false;
}
}
/**
* Set the template to be used by this model
*
* #param string $template
* #return ViewModel
*/
public function setTemplate($template)
{
$this->template = (string) $template;
if ( $this->customInstanceName === false) {
return $this;
}
$pathComponents = explode('/', (string) $template);
$last = array_pop($pathComponents);
array_push($pathComponents, $this->customInstanceName);
array_push($pathComponents, $last);
$customTemplate = implode('/', $pathComponents);
if ($this->pathStack->resolve($customTemplate) !== false) {
$this->template = $customTemplate;
}
return $this;
}
}
Using the "Decorator Pattern" I can achieve the same customization level on my Models.
I'm having problem to handle specific behavior. In this case I plan to create custom Controllers extending
my base controller class, but I'unable to call those controllers since the routing is defined on the module
config (and I was unable to change it in runtime).
My questions are:
1) Is this approach correct, or there is a better way to do it?
2) If the approach is correct, how can I define a custom router to be used when the ServiceManager reads my routing config?
Just found a solution. Will register it here hoping someone will benefit from it.
All I had to do was to create a specific router class with a match method which returns the correct routing target for each customer controller, and add it to my module.config.php as the type for each action.
namespace TARGETNAMESPACE;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Mvc\Router\Http\Literal;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class MyRouterLiteral extends Literal {
public function match(Request $request, $pathOffset = null) {
if (! method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if ($pathOffset !== null) {
if ($pathOffset >= 0 && strlen($path) >= $pathOffset && ! empty($this->route)) {
if (strpos($path, $this->route, $pathOffset) === $pathOffset) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
}
return null;
}
if ($path === $this->route) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
return null;
}
private function getDefaults() {
$aux = explode('\\', $this->defaults['controller']);
$last = array_pop($aux);
array_push($aux, '[CUSTOM_INSTANCE_NAME]');
array_push($aux, '[CUSTOM_INSTANCE_NAME]'.$last);
$result = $this->defaults;
$result['controller'] = implode('\\', $aux);
return $result;
}
}
To address all cases I had to create a second custom router (for segment routes) which follows the same rules and can be easily derived from the above code.

zend framework 2 + custom routing

I tried to follow the recommendations from this topic: zend framework 2 + routing database
I have a route class:
namespace Application\Router;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\Mvc\Router\RouteMatch;
class Content implements RouteInterface, ServiceLocatorAwareInterface {
protected $defaults = array();
protected $routerPluginManager = null;
public function __construct(array $defaults = array()) {
$this->defaults = $defaults;
}
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $routerPluginManager) {
$this->routerPluginManager = $routerPluginManager;
}
public function getServiceLocator() {
return $this->routerPluginManager;
}
public static function factory($options = array()) {
if ($options instanceof \Traversable) {
$options = ArrayUtils::iteratorToArray($options);
} elseif (!is_array($options)) {
throw new InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options');
}
if (!isset($options['defaults'])) {
$options['defaults'] = array();
}
return new static($options['defaults']);
}
public function match(Request $request, $pathOffset = null) {
if (!method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$fullPath = $uri->getPath();
$path = substr($fullPath, $pathOffset);
$alias = trim($path, '/');
$options = $this->defaults;
$options = array_merge($options, array(
'path' => $alias
));
return new RouteMatch($options);
}
public function assemble(array $params = array(), array $options = array()) {
if (array_key_exists('path', $params)) {
return '/' . $params['path'];
}
return '/';
}
public function getAssembledParams() {
return array();
}
}
Pay attention that the match() function returns object of the instance of Zend\Mvc\Router\RouteMatch
However in the file Zend\Mvc\Router\Http\TreeRouteStack it checks for object to be the instance of RouteMatch (without prefix of namespace)
if (
($match = $route->match($request, $baseUrlLength, $options)) instanceof RouteMatch
&& ($pathLength === null || $match->getLength() === $pathLength)
)
And the condition fails in my case because of the namespace.
Any suggestions?
Ok, i figured out what the problem was.
Instead of returning Zend\Mvc\Router\RouteMatch I should return Zend\Mvc\Router\Http\RouteMatch
This fixed my problem

Best way to do multiple constructors in PHP

You can't put two __construct functions with unique argument signatures in a PHP class. I'd like to do this:
class Student
{
protected $id;
protected $name;
// etc.
public function __construct($id){
$this->id = $id;
// other members are still uninitialized
}
public function __construct($row_from_database){
$this->id = $row_from_database->id;
$this->name = $row_from_database->name;
// etc.
}
}
What is the best way to do this in PHP?
I'd probably do something like this:
<?php
class Student
{
public function __construct() {
// allocate your stuff
}
public static function withID( $id ) {
$instance = new self();
$instance->loadByID( $id );
return $instance;
}
public static function withRow( array $row ) {
$instance = new self();
$instance->fill( $row );
return $instance;
}
protected function loadByID( $id ) {
// do query
$row = my_awesome_db_access_stuff( $id );
$this->fill( $row );
}
protected function fill( array $row ) {
// fill all properties from array
}
}
?>
Then if i want a Student where i know the ID:
$student = Student::withID( $id );
Or if i have an array of the db row:
$student = Student::withRow( $row );
Technically you're not building multiple constructors, just static helper methods, but you get to avoid a lot of spaghetti code in the constructor this way.
The solution of Kris is really nice, but I prefer a mix of factory and fluent style:
<?php
class Student
{
protected $firstName;
protected $lastName;
// etc.
/**
* Constructor
*/
public function __construct() {
// allocate your stuff
}
/**
* Static constructor / factory
*/
public static function create() {
return new self();
}
/**
* FirstName setter - fluent style
*/
public function setFirstName($firstName) {
$this->firstName = $firstName;
return $this;
}
/**
* LastName setter - fluent style
*/
public function setLastName($lastName) {
$this->lastName = $lastName;
return $this;
}
}
// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");
// see result
var_dump($student);
?>
PHP is a dynamic language, so you can't overload methods. You have to check the types of your argument like this:
class Student
{
protected $id;
protected $name;
// etc.
public function __construct($idOrRow){
if(is_int($idOrRow))
{
$this->id = $idOrRow;
// other members are still uninitialized
}
else if(is_array($idOrRow))
{
$this->id = $idOrRow->id;
$this->name = $idOrRow->name;
// etc.
}
}
As has already been shown here, there are many ways of declaring multiple constructors in PHP, but none of them are the correct way of doing so (since PHP technically doesn't allow it).
But it doesn't stop us from hacking this functionality...
Here's another example:
<?php
class myClass {
public function __construct() {
$get_arguments = func_get_args();
$number_of_arguments = func_num_args();
if (method_exists($this, $method_name = '__construct'.$number_of_arguments)) {
call_user_func_array(array($this, $method_name), $get_arguments);
}
}
public function __construct1($argument1) {
echo 'constructor with 1 parameter ' . $argument1 . "\n";
}
public function __construct2($argument1, $argument2) {
echo 'constructor with 2 parameter ' . $argument1 . ' ' . $argument2 . "\n";
}
public function __construct3($argument1, $argument2, $argument3) {
echo 'constructor with 3 parameter ' . $argument1 . ' ' . $argument2 . ' ' . $argument3 . "\n";
}
}
$object1 = new myClass('BUET');
$object2 = new myClass('BUET', 'is');
$object3 = new myClass('BUET', 'is', 'Best.');
Source: The easiest way to use and understand multiple constructors:
Hope this helps. :)
public function __construct() {
$parameters = func_get_args();
...
}
$o = new MyClass('One', 'Two', 3);
Now $paramters will be an array with the values 'One', 'Two', 3.
Edit,
I can add that
func_num_args()
will give you the number of parameters to the function.
You could do something like this:
public function __construct($param)
{
if(is_int($param)) {
$this->id = $param;
} elseif(is_object($param)) {
// do something else
}
}
As of version 5.4, PHP supports traits. This is not exactly what you are looking for, but a simplistic trait based approach would be:
trait StudentTrait {
protected $id;
protected $name;
final public function setId($id) {
$this->id = $id;
return $this;
}
final public function getId() { return $this->id; }
final public function setName($name) {
$this->name = $name;
return $this;
}
final public function getName() { return $this->name; }
}
class Student1 {
use StudentTrait;
final public function __construct($id) { $this->setId($id); }
}
class Student2 {
use StudentTrait;
final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}
We end up with two classes, one for each constructor, which is a bit counter-productive. To maintain some sanity, I'll throw in a factory:
class StudentFactory {
static public function getStudent($id, $name = null) {
return
is_null($name)
? new Student1($id)
: new Student2($id, $name)
}
}
So, it all comes down to this:
$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");
It's a horribly verbose approach, but it can be extremely convenient.
Here is an elegant way to do it. Create trait that will enable multiple constructors given the number of parameters. You would simply add the number of parameters to the function name "__construct". So one parameter will be "__construct1", two "__construct2"... etc.
trait constructable
{
public function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array([$this,$f],$a);
}
}
}
class a{
use constructable;
public $result;
public function __construct1($a){
$this->result = $a;
}
public function __construct2($a, $b){
$this->result = $a + $b;
}
}
echo (new a(1))->result; // 1
echo (new a(1,2))->result; // 3
Another option is to use default arguments in the constructor like this
class Student {
private $id;
private $name;
//...
public function __construct($id, $row=array()) {
$this->id = $id;
foreach($row as $key => $value) $this->$key = $value;
}
}
This means you'll need to instantiate with a row like this: $student = new Student($row['id'], $row) but keeps your constructor nice and clean.
On the other hand, if you want to make use of polymorphism then you can create two classes like so:
class Student {
public function __construct($row) {
foreach($row as $key => $value) $this->$key = $value;
}
}
class EmptyStudent extends Student {
public function __construct($id) {
parent::__construct(array('id' => $id));
}
}
as stated in the other comments, as php does not support overloading, usually the "type checking tricks" in constructor are avoided and the factory pattern is used intead
ie.
$myObj = MyClass::factory('fromInteger', $params);
$myObj = MyClass::factory('fromRow', $params);
You could do something like the following which is really easy and very clean:
public function __construct()
{
$arguments = func_get_args();
switch(sizeof(func_get_args()))
{
case 0: //No arguments
break;
case 1: //One argument
$this->do_something($arguments[0]);
break;
case 2: //Two arguments
$this->do_something_else($arguments[0], $arguments[1]);
break;
}
}
This question has already been answered with very smart ways to fulfil the requirement but I am wondering why not take a step back and ask the basic question of why do we need a class with two constructors?
If my class needs two constructors then probably the way I am designing my classes needs little more consideration to come up with a design that is cleaner and more testable.
We are trying to mix up how to instantiate a class with the actual class logic.
If a Student object is in a valid state, then does it matter if it was constructed from the row of a DB or data from a web form or a cli request?
Now to answer the question that that may arise here that if we don't add the logic of creating an object from db row, then how do we create an object from the db data, we can simply add another class, call it StudentMapper if you are comfortable with data mapper pattern, in some cases you can use StudentRepository, and if nothing fits your needs you can make a StudentFactory to handle all kinds of object construction tasks.
Bottomline is to keep persistence layer out of our head when we are working on the domain objects.
I know I'm super late to the party here, but I came up with a fairly flexible pattern that should allow some really interesting and versatile implementations.
Set up your class as you normally would, with whatever variables you like.
class MyClass{
protected $myVar1;
protected $myVar2;
public function __construct($obj = null){
if($obj){
foreach (((object)$obj) as $key => $value) {
if(isset($value) && in_array($key, array_keys(get_object_vars($this)))){
$this->$key = $value;
}
}
}
}
}
When you make your object just pass an associative array with the keys of the array the same as the names of your vars, like so...
$sample_variable = new MyClass([
'myVar2'=>123,
'i_dont_want_this_one'=> 'This won\'t make it into the class'
]);
print_r($sample_variable);
The print_r($sample_variable); after this instantiation yields the following:
MyClass Object ( [myVar1:protected] => [myVar2:protected] => 123 )
Because we've initialize $group to null in our __construct(...), it is also valid to pass nothing whatsoever into the constructor as well, like so...
$sample_variable = new MyClass();
print_r($sample_variable);
Now the output is exactly as expected:
MyClass Object ( [myVar1:protected] => [myVar2:protected] => )
The reason I wrote this was so that I could directly pass the output of json_decode(...) to my constructor, and not worry about it too much.
This was executed in PHP 7.1. Enjoy!
I was facing the same issue on creating multiple constructors with different signatures but unfortunately, PHP doesn't offer a direct method to do so. Howerever, I found a trick to overcome that. Hope works for all of you too.
<?PHP
class Animal
{
public function __construct()
{
$arguments = func_get_args();
$numberOfArguments = func_num_args();
if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
call_user_func_array(array($this, $function), $arguments);
}
}
public function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
public function __construct2($a1, $a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
public function __construct3($a1, $a2, $a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new Animal('sheep');
$o = new Animal('sheep','cat');
$o = new Animal('sheep','cat','dog');
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
This is my take on it (build for php 5.6).
It will look at constructor parameter types (array, class name, no description) and compare the given arguments. Constructors must be given with least specificity last. With examples:
// demo class
class X {
public $X;
public function __construct($x) {
$this->X = $x;
}
public function __toString() {
return 'X'.$this->X;
}
}
// demo class
class Y {
public $Y;
public function __construct($y) {
$this->Y = $y;
}
public function __toString() {
return 'Y'.$this->Y;
}
}
// here be magic
abstract class MultipleConstructors {
function __construct() {
$__get_arguments = func_get_args();
$__number_of_arguments = func_num_args();
$__reflect = new ReflectionClass($this);
foreach($__reflect->getMethods() as $__reflectmethod) {
$__method_name = $__reflectmethod->getName();
if (substr($__method_name, 0, strlen('__construct')) === '__construct') {
$__parms = $__reflectmethod->getParameters();
if (count($__parms) == $__number_of_arguments) {
$__argsFit = true;
foreach ($__parms as $__argPos => $__param) {
$__paramClass= $__param->getClass();
$__argVar = func_get_arg($__argPos);
$__argVarType = gettype($__argVar);
$__paramIsArray = $__param->isArray() == true;
$__argVarIsArray = $__argVarType == 'array';
// parameter is array and argument isn't, or the other way around.
if (($__paramIsArray && !$__argVarIsArray) ||
(!$__paramIsArray && $__argVarIsArray)) {
$__argsFit = false;
continue;
}
// class check
if ((!is_null($__paramClass) && $__argVarType != 'object') ||
(is_null($__paramClass) && $__argVarType == 'object')){
$__argsFit = false;
continue;
}
if (!is_null($__paramClass) && $__argVarType == 'object') {
// class type check
$__paramClassName = "N/A";
if ($__paramClass)
$__paramClassName = $__paramClass->getName();
if ($__paramClassName != get_class($__argVar)) {
$__argsFit = false;
}
}
}
if ($__argsFit) {
call_user_func_array(array($this, $__method_name), $__get_arguments);
return;
}
}
}
}
throw new Exception("No matching constructors");
}
}
// how to use multiple constructors
class A extends MultipleConstructors {
public $value;
function __constructB(array $hey) {
$this->value = 'Array#'.count($hey).'<br/>';
}
function __construct1(X $first) {
$this->value = $first .'<br/>';
}
function __construct2(Y $second) {
$this->value = $second .'<br/>';
}
function __constructA($hey) {
$this->value = $hey.'<br/>';
}
function __toString() {
return $this->value;
}
}
$x = new X("foo");
$y = new Y("bar");
$aa = new A(array("one", "two", "three"));
echo $aa;
$ar = new A("baz");
echo $ar;
$ax = new A($x);
echo $ax;
$ay = new A($y);
echo $ay;
Result:
Array#3
baz
Xfoo
Ybar
Instead of the terminating exception if no constructor is found, it could be remove and allow for "empty" constructor. Or whatever you like.
Let me add my grain of sand here
I personally like adding a constructors as static functions that return an instance of the class (the object). The following code is an example:
class Person
{
private $name;
private $email;
public static function withName($name)
{
$person = new Person();
$person->name = $name;
return $person;
}
public static function withEmail($email)
{
$person = new Person();
$person->email = $email;
return $person;
}
}
Note that now you can create instance of the Person class like this:
$person1 = Person::withName('Example');
$person2 = Person::withEmail('yo#mi_email.com');
I took that code from:
http://alfonsojimenez.com/post/30377422731/multiple-constructors-in-php
Hmm, surprised I don't see this answer yet, suppose I'll throw my hat in the ring.
class Action {
const cancelable = 0;
const target = 1
const type = 2;
public $cancelable;
public $target;
public $type;
__construct( $opt = [] ){
$this->cancelable = isset($opt[cancelable]) ? $opt[cancelable] : true;
$this->target = isset($opt[target]) ? $opt[target] : NULL;
$this->type = isset($opt[type]) ? $opt[type] : 'action';
}
}
$myAction = new Action( [
Action::cancelable => false,
Action::type => 'spin',
.
.
.
]);
You can optionally separate the options into their own class, such as extending SplEnum.
abstract class ActionOpt extends SplEnum{
const cancelable = 0;
const target = 1
const type = 2;
}
Starting with PHP 8 we can use named arguments:
class Student {
protected int $id;
protected string $name;
public function __construct(int $id = null, string $name = null, array $row_from_database = null) {
if ($id !== null && $name !== null && $row_from_database === null) {
$this->id = $id;
$this->name = $name;
} elseif ($id === null && $name === null
&& $row_from_database !== null
&& array_keys($row_from_database) === [ 'id', 'name' ]
&& is_int($row_from_database['id'])
&& is_string($row_from_database['name'])) {
$this->id = $row_from_database['id'];
$this->name = $row_from_database['name'];
} else {
throw new InvalidArgumentException('Invalid arguments');
}
}
}
$student1 = new Student(id: 3, name: 'abc');
$student2 = new Student(row_from_database: [ 'id' => 4, 'name' => 'def' ]);
With proper checking it is possible to rule out invalid combinations of arguments, so that the created instance is a valid one at the end of the constructor (but errors will only be detected at runtime).
For php7, I compare parameters type as well, you can have two constructors with same number of parameters but different type.
trait GenericConstructorOverloadTrait
{
/**
* #var array Constructors metadata
*/
private static $constructorsCache;
/**
* Generic constructor
* GenericConstructorOverloadTrait constructor.
*/
public function __construct()
{
$params = func_get_args();
$numParams = func_num_args();
$finish = false;
if(!self::$constructorsCache){
$class = new \ReflectionClass($this);
$constructors = array_filter($class->getMethods(),
function (\ReflectionMethod $method) {
return preg_match("/\_\_construct[0-9]+/",$method->getName());
});
self::$constructorsCache = $constructors;
}
else{
$constructors = self::$constructorsCache;
}
foreach($constructors as $constructor){
$reflectionParams = $constructor->getParameters();
if(count($reflectionParams) != $numParams){
continue;
}
$matched = true;
for($i=0; $i< $numParams; $i++){
if($reflectionParams[$i]->hasType()){
$type = $reflectionParams[$i]->getType()->__toString();
}
if(
!(
!$reflectionParams[$i]->hasType() ||
($reflectionParams[$i]->hasType() &&
is_object($params[$i]) &&
$params[$i] instanceof $type) ||
($reflectionParams[$i]->hasType() &&
$reflectionParams[$i]->getType()->__toString() ==
gettype($params[$i]))
)
) {
$matched = false;
break;
}
}
if($matched){
call_user_func_array(array($this,$constructor->getName()),
$params);
$finish = true;
break;
}
}
unset($constructor);
if(!$finish){
throw new \InvalidArgumentException("Cannot match construct by params");
}
}
}
To use it:
class MultiConstructorClass{
use GenericConstructorOverloadTrait;
private $param1;
private $param2;
private $param3;
public function __construct1($param1, array $param2)
{
$this->param1 = $param1;
$this->param2 = $param2;
}
public function __construct2($param1, array $param2, \DateTime $param3)
{
$this->__construct1($param1, $param2);
$this->param3 = $param3;
}
/**
* #return \DateTime
*/
public function getParam3()
{
return $this->param3;
}
/**
* #return array
*/
public function getParam2()
{
return $this->param2;
}
/**
* #return mixed
*/
public function getParam1()
{
return $this->param1;
}
}
More modern aproach:
You are mixing seperate classes into one, entity & data hydration.
So for your case you should have 2 classes:
class Student
{
protected $id;
protected $name;
// etc.
}
class StudentHydrator
{
public function hydrate(Student $student, array $data){
$student->setId($data['id']);
if(isset($data['name')){
$student->setName($data['name']);
}
// etc. Can be replaced with foreach
return $student;
}
}
//usage
$hydrator = new StudentHydrator();
$student = $hydrator->hydrate(new Student(), ['id'=>4]);
$student2 = $hydrator->hydrate(new Student(), $rowFromDB);
Also please note that you should use doctrine or other ORM that already provides automatic entity hydration.
And you should use dependency injection in order to skip mannualy creating objects like StudentHydrator.
Kris's answer is great, but as Buttle Butku commented, new static() would be preferred in PHP 5.3+.
So I'd do it like this (modified from Kris's answer):
<?php
class Student
{
public function __construct() {
// allocate your stuff
}
public static function withID( $id ) {
$instance = new static();
$instance->loadByID( $id );
return $instance;
}
public static function withRow( array $row ) {
$instance = new static();
$instance->fill( $row );
return $instance;
}
protected function loadByID( $id ) {
// do query
$row = my_awesome_db_access_stuff( $id );
$this->fill( $row );
}
protected function fill( array $row ) {
// fill all properties from array
}
}
?>
Usage:
<?php
$student1 = Student::withID($id);
$student2 = Student::withRow($row);
?>
I also found an useful example in php.net OOP document.
In response to the best answer by Kris (which amazingly helped design my own class btw), here is a modified version for those that might find it useful. Includes methods for selecting from any column and dumping object data from array. Cheers!
public function __construct() {
$this -> id = 0;
//...
}
public static function Exists($id) {
if (!$id) return false;
$id = (int)$id;
if ($id <= 0) return false;
$mysqli = Mysql::Connect();
if (mysqli_num_rows(mysqli_query($mysqli, "SELECT id FROM users WHERE id = " . $id)) == 1) return true;
return false;
}
public static function FromId($id) {
$u = new self();
if (!$u -> FillFromColumn("id", $id)) return false;
return $u;
}
public static function FromColumn($column, $value) {
$u = new self();
if (!$u -> FillFromColumn($column, $value)) return false;
return $u;
}
public static function FromArray($row = array()) {
if (!is_array($row) || $row == array()) return false;
$u = new self();
$u -> FillFromArray($row);
return $u;
}
protected function FillFromColumn($column, $value) {
$mysqli = Mysql::Connect();
//Assuming we're only allowed to specified EXISTENT columns
$result = mysqli_query($mysqli, "SELECT * FROM users WHERE " . $column . " = '" . $value . "'");
$count = mysqli_num_rows($result);
if ($count == 0) return false;
$row = mysqli_fetch_assoc($result);
$this -> FillFromArray($row);
}
protected function FillFromArray(array $row) {
foreach($row as $i => $v) {
if (isset($this -> $i)) {
$this -> $i = $v;
}
}
}
public function ToArray() {
$m = array();
foreach ($this as $i => $v) {
$m[$i] = $v;
}
return $m;
}
public function Dump() {
print_r("<PRE>");
print_r($this -> ToArray());
print_r("</PRE>");
}
Call constructors by data type:
class A
{
function __construct($argument)
{
$type = gettype($argument);
if($type == 'unknown type')
{
// type unknown
}
$this->{'__construct_'.$type}($argument);
}
function __construct_boolean($argument)
{
// do something
}
function __construct_integer($argument)
{
// do something
}
function __construct_double($argument)
{
// do something
}
function __construct_string($argument)
{
// do something
}
function __construct_array($argument)
{
// do something
}
function __construct_object($argument)
{
// do something
}
function __construct_resource($argument)
{
// do something
}
// other functions
}
You could always add an extra parameter to the constructor called something like mode and then perform a switch statement on it...
class myClass
{
var $error ;
function __construct ( $data, $mode )
{
$this->error = false
switch ( $mode )
{
'id' : processId ( $data ) ; break ;
'row' : processRow ( $data ); break ;
default : $this->error = true ; break ;
}
}
function processId ( $data ) { /* code */ }
function processRow ( $data ) { /* code */ }
}
$a = new myClass ( $data, 'id' ) ;
$b = new myClass ( $data, 'row' ) ;
$c = new myClass ( $data, 'something' ) ;
if ( $a->error )
exit ( 'invalid mode' ) ;
if ( $b->error )
exit ('invalid mode' ) ;
if ( $c->error )
exit ('invalid mode' ) ;
Also with that method at any time if you wanted to add more functionality you can just add another case to the switch statement, and you can also check to make sure someone has sent the right thing through - in the above example all the data is ok except for C as that is set to "something" and so the error flag in the class is set and control is returned back to the main program for it to decide what to do next (in the example I just told it to exit with an error message "invalid mode" - but alternatively you could loop it back round until valid data is found).
I created this method to let use it not only on constructors but in methods:
My constructor:
function __construct() {
$paramsNumber=func_num_args();
if($paramsNumber==0){
//do something
}else{
$this->overload('__construct',func_get_args());
}
}
My doSomething method:
public function doSomething() {
$paramsNumber=func_num_args();
if($paramsNumber==0){
//do something
}else{
$this->overload('doSomething',func_get_args());
}
}
Both works with this simple method:
public function overloadMethod($methodName,$params){
$paramsNumber=sizeof($params);
//methodName1(), methodName2()...
$methodNameNumber =$methodName.$paramsNumber;
if (method_exists($this,$methodNameNumber)) {
call_user_func_array(array($this,$methodNameNumber),$params);
}
}
So you can declare
__construct1($arg1), __construct2($arg1,$arg2)...
or
methodName1($arg1), methodName2($arg1,$arg2)...
and so on :)
And when using:
$myObject = new MyClass($arg1, $arg2,..., $argN);
it will call __constructN, where you defined N args
then
$myObject -> doSomething($arg1, $arg2,..., $argM)
it will call doSomethingM, , where you defined M args;

Categories