CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.1 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.1
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • ConsoleErrorHandler
  • ConsoleInput
  • ConsoleInputArgument
  • ConsoleInputOption
  • ConsoleInputSubcommand
  • ConsoleOptionParser
  • ConsoleOutput
  • HelpFormatter
  • Shell
  • ShellDispatcher
  • TaskCollection
  1: <?php
  2: /**
  3:  * ConsoleOptionParser file
  4:  *
  5:  * PHP 5
  6:  *
  7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9:  *
 10:  * Licensed under The MIT License
 11:  * Redistributions of files must retain the above copyright notice.
 12:  *
 13:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 14:  * @link          http://cakephp.org CakePHP(tm) Project
 15:  * @since         CakePHP(tm) v 2.0
 16:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 17:  */
 18: 
 19: App::uses('TaskCollection', 'Console');
 20: App::uses('ConsoleOutput', 'Console');
 21: App::uses('ConsoleInput', 'Console');
 22: App::uses('ConsoleInputSubcommand', 'Console');
 23: App::uses('ConsoleInputOption', 'Console');
 24: App::uses('ConsoleInputArgument', 'Console');
 25: App::uses('ConsoleOptionParser', 'Console');
 26: App::uses('HelpFormatter', 'Console');
 27: 
 28: /**
 29:  * Handles parsing the ARGV in the command line and provides support
 30:  * for GetOpt compatible option definition.  Provides a builder pattern implementation
 31:  * for creating shell option parsers.
 32:  *
 33:  * ### Options
 34:  *
 35:  * Named arguments come in two forms, long and short. Long arguments are preceded
 36:  * by two - and give a more verbose option name. i.e. `--version`. Short arguments are
 37:  * preceded by one - and are only one character long.  They usually match with a long option,
 38:  * and provide a more terse alternative.
 39:  *
 40:  * ### Using Options
 41:  *
 42:  * Options can be defined with both long and short forms.  By using `$parser->addOption()`
 43:  * you can define new options.  The name of the option is used as its long form, and you
 44:  * can supply an additional short form, with the `short` option. Short options should
 45:  * only be one letter long.  Using more than one letter for a short option will raise an exception.
 46:  *
 47:  * Calling options can be done using syntax similar to most *nix command line tools. Long options
 48:  * cane either include an `=` or leave it out.
 49:  *
 50:  * `cake myshell command --connection default --name=something`
 51:  *
 52:  * Short options can be defined signally or in groups.
 53:  *
 54:  * `cake myshell command -cn`
 55:  *
 56:  * Short options can be combined into groups as seen above.  Each letter in a group
 57:  * will be treated as a separate option.  The previous example is equivalent to:
 58:  *
 59:  * `cake myshell command -c -n`
 60:  *
 61:  * Short options can also accept values:
 62:  *
 63:  * `cake myshell command -c default`
 64:  *
 65:  * ### Positional arguments
 66:  *
 67:  * If no positional arguments are defined, all of them will be parsed.  If you define positional
 68:  * arguments any arguments greater than those defined will cause exceptions.  Additionally you can
 69:  * declare arguments as optional, by setting the required param to false.
 70:  *
 71:  * `$parser->addArgument('model', array('required' => false));`
 72:  *
 73:  * ### Providing Help text
 74:  *
 75:  * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser
 76:  * can generate a help display for you.  You can view the help for shells by using the `--help` or `-h` switch.
 77:  *
 78:  * @package       Cake.Console
 79:  */
 80: class ConsoleOptionParser {
 81: 
 82: /**
 83:  * Description text - displays before options when help is generated
 84:  *
 85:  * @see ConsoleOptionParser::description()
 86:  * @var string
 87:  */
 88:     protected $_description = null;
 89: 
 90: /**
 91:  * Epilog text - displays after options when help is generated
 92:  *
 93:  * @see ConsoleOptionParser::epilog()
 94:  * @var string
 95:  */
 96:     protected $_epilog = null;
 97: 
 98: /**
 99:  * Option definitions.
100:  *
101:  * @see ConsoleOptionParser::addOption()
102:  * @var array
103:  */
104:     protected $_options = array();
105: 
106: /**
107:  * Map of short -> long options, generated when using addOption()
108:  *
109:  * @var string
110:  */
111:     protected $_shortOptions = array();
112: 
113: /**
114:  * Positional argument definitions.
115:  *
116:  * @see ConsoleOptionParser::addArgument()
117:  * @var array
118:  */
119:     protected $_args = array();
120: 
121: /**
122:  * Subcommands for this Shell.
123:  *
124:  * @see ConsoleOptionParser::addSubcommand()
125:  * @var array
126:  */
127:     protected $_subcommands = array();
128: 
129: /**
130:  * Command name.
131:  *
132:  * @var string
133:  */
134:     protected $_command = '';
135: 
136: /**
137:  * Construct an OptionParser so you can define its behavior
138:  *
139:  * @param string $command The command name this parser is for.  The command name is used for generating help.
140:  * @param boolean $defaultOptions Whether you want the verbose and quiet options set. Setting
141:  *  this to false will prevent the addition of `--verbose` & `--quiet` options.
142:  */
143:     public function __construct($command = null, $defaultOptions = true) {
144:         $this->command($command);
145: 
146:         $this->addOption('help', array(
147:             'short' => 'h',
148:             'help' => __d('cake_console', 'Display this help.'),
149:             'boolean' => true
150:         ));
151: 
152:         if ($defaultOptions) {
153:             $this->addOption('verbose', array(
154:                 'short' => 'v',
155:                 'help' => __d('cake_console', 'Enable verbose output.'),
156:                 'boolean' => true
157:             ))->addOption('quiet', array(
158:                 'short' => 'q',
159:                 'help' => __d('cake_console', 'Enable quiet output.'),
160:                 'boolean' => true
161:             ));
162:         }
163:     }
164: 
165: /**
166:  * Static factory method for creating new OptionParsers so you can chain methods off of them.
167:  *
168:  * @param string $command The command name this parser is for.  The command name is used for generating help.
169:  * @param boolean $defaultOptions Whether you want the verbose and quiet options set.
170:  * @return ConsoleOptionParser
171:  */
172:     public static function create($command, $defaultOptions = true) {
173:         return new ConsoleOptionParser($command, $defaultOptions);
174:     }
175: 
176: /**
177:  * Build a parser from an array. Uses an array like
178:  *
179:  * {{{
180:  * $spec = array(
181:  *      'description' => 'text',
182:  *      'epilog' => 'text',
183:  *      'arguments' => array(
184:  *          // list of arguments compatible with addArguments.
185:  *      ),
186:  *      'options' => array(
187:  *          // list of options compatible with addOptions
188:  *      ),
189:  *      'subcommands' => array(
190:  *          // list of subcommands to add.
191:  *      )
192:  * );
193:  * }}}
194:  *
195:  * @param array $spec The spec to build the OptionParser with.
196:  * @return ConsoleOptionParser
197:  */
198:     public static function buildFromArray($spec) {
199:         $parser = new ConsoleOptionParser($spec['command']);
200:         if (!empty($spec['arguments'])) {
201:             $parser->addArguments($spec['arguments']);
202:         }
203:         if (!empty($spec['options'])) {
204:             $parser->addOptions($spec['options']);
205:         }
206:         if (!empty($spec['subcommands'])) {
207:             $parser->addSubcommands($spec['subcommands']);
208:         }
209:         if (!empty($spec['description'])) {
210:             $parser->description($spec['description']);
211:         }
212:         if (!empty($spec['epilog'])) {
213:             $parser->epilog($spec['epilog']);
214:         }
215:         return $parser;
216:     }
217: 
218: /**
219:  * Get or set the command name for shell/task.
220:  *
221:  * @param string $text The text to set, or null if you want to read
222:  * @return mixed If reading, the value of the command. If setting $this will be returned
223:  */
224:     public function command($text = null) {
225:         if ($text !== null) {
226:             $this->_command = Inflector::underscore($text);
227:             return $this;
228:         }
229:         return $this->_command;
230:     }
231: 
232: /**
233:  * Get or set the description text for shell/task.
234:  *
235:  * @param mixed $text The text to set, or null if you want to read. If an array the
236:  *   text will be imploded with "\n"
237:  * @return mixed If reading, the value of the description. If setting $this will be returned
238:  */
239:     public function description($text = null) {
240:         if ($text !== null) {
241:             if (is_array($text)) {
242:                 $text = implode("\n", $text);
243:             }
244:             $this->_description = $text;
245:             return $this;
246:         }
247:         return $this->_description;
248:     }
249: 
250: /**
251:  * Get or set an epilog to the parser.  The epilog is added to the end of
252:  * the options and arguments listing when help is generated.
253:  *
254:  * @param mixed $text Text when setting or null when reading. If an array the text will be imploded with "\n"
255:  * @return mixed If reading, the value of the epilog. If setting $this will be returned.
256:  */
257:     public function epilog($text = null) {
258:         if ($text !== null) {
259:             if (is_array($text)) {
260:                 $text = implode("\n", $text);
261:             }
262:             $this->_epilog = $text;
263:             return $this;
264:         }
265:         return $this->_epilog;
266:     }
267: 
268: /**
269:  * Add an option to the option parser. Options allow you to define optional or required
270:  * parameters for your console application. Options are defined by the parameters they use.
271:  *
272:  * ### Options
273:  *
274:  * - `short` - The single letter variant for this option, leave undefined for none.
275:  * - `help` - Help text for this option.  Used when generating help for the option.
276:  * - `default` - The default value for this option. Defaults are added into the parsed params when the
277:  *    attached option is not provided or has no value.  Using default and boolean together will not work.
278:  *    are added into the parsed parameters when the option is undefined. Defaults to null.
279:  * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false.
280:  *    If an option is defined as boolean, it will always be added to the parsed params.  If no present
281:  *    it will be false, if present it will be true.
282:  * - `choices` A list of valid choices for this option.  If left empty all values are valid..
283:  *   An exception will be raised when parse() encounters an invalid value.
284:  *
285:  * @param mixed $name The long name you want to the value to be parsed out as when options are parsed.
286:  *   Will also accept an instance of ConsoleInputOption
287:  * @param array $options An array of parameters that define the behavior of the option
288:  * @return ConsoleOptionParser $this.
289:  */
290:     public function addOption($name, $options = array()) {
291:         if (is_object($name) && $name instanceof ConsoleInputOption) {
292:             $option = $name;
293:             $name = $option->name();
294:         } else {
295:             $defaults = array(
296:                 'name' => $name,
297:                 'short' => null,
298:                 'help' => '',
299:                 'default' => null,
300:                 'boolean' => false,
301:                 'choices' => array()
302:             );
303:             $options = array_merge($defaults, $options);
304:             $option = new ConsoleInputOption($options);
305:         }
306:         $this->_options[$name] = $option;
307:         if ($option->short() !== null) {
308:             $this->_shortOptions[$option->short()] = $name;
309:         }
310:         return $this;
311:     }
312: 
313: /**
314:  * Add a positional argument to the option parser.
315:  *
316:  * ### Params
317:  *
318:  * - `help` The help text to display for this argument.
319:  * - `required` Whether this parameter is required.
320:  * - `index` The index for the arg, if left undefined the argument will be put
321:  *   onto the end of the arguments. If you define the same index twice the first
322:  *   option will be overwritten.
323:  * - `choices` A list of valid choices for this argument.  If left empty all values are valid..
324:  *   An exception will be raised when parse() encounters an invalid value.
325:  *
326:  * @param mixed $name The name of the argument.  Will also accept an instance of ConsoleInputArgument
327:  * @param array $params Parameters for the argument, see above.
328:  * @return ConsoleOptionParser $this.
329:  */
330:     public function addArgument($name, $params = array()) {
331:         if (is_object($name) && $name instanceof ConsoleInputArgument) {
332:             $arg = $name;
333:             $index = count($this->_args);
334:         } else {
335:             $defaults = array(
336:                 'name' => $name,
337:                 'help' => '',
338:                 'index' => count($this->_args),
339:                 'required' => false,
340:                 'choices' => array()
341:             );
342:             $options = array_merge($defaults, $params);
343:             $index = $options['index'];
344:             unset($options['index']);
345:             $arg = new ConsoleInputArgument($options);
346:         }
347:         $this->_args[$index] = $arg;
348:         return $this;
349:     }
350: 
351: /**
352:  * Add multiple arguments at once. Take an array of argument definitions.
353:  * The keys are used as the argument names, and the values as params for the argument.
354:  *
355:  * @param array $args Array of arguments to add.
356:  * @see ConsoleOptionParser::addArgument()
357:  * @return ConsoleOptionParser $this
358:  */
359:     public function addArguments(array $args) {
360:         foreach ($args as $name => $params) {
361:             $this->addArgument($name, $params);
362:         }
363:         return $this;
364:     }
365: 
366: /**
367:  * Add multiple options at once. Takes an array of option definitions.
368:  * The keys are used as option names, and the values as params for the option.
369:  *
370:  * @param array $options Array of options to add.
371:  * @see ConsoleOptionParser::addOption()
372:  * @return ConsoleOptionParser $this
373:  */
374:     public function addOptions(array $options) {
375:         foreach ($options as $name => $params) {
376:             $this->addOption($name, $params);
377:         }
378:         return $this;
379:     }
380: 
381: /**
382:  * Append a subcommand to the subcommand list.
383:  * Subcommands are usually methods on your Shell, but can also be used to document Tasks.
384:  *
385:  * ### Options
386:  *
387:  * - `help` - Help text for the subcommand.
388:  * - `parser` - A ConsoleOptionParser for the subcommand.  This allows you to create method
389:  *    specific option parsers.  When help is generated for a subcommand, if a parser is present
390:  *    it will be used.
391:  *
392:  * @param mixed $name Name of the subcommand. Will also accept an instance of ConsoleInputSubcommand
393:  * @param array $options Array of params, see above.
394:  * @return ConsoleOptionParser $this.
395:  */
396:     public function addSubcommand($name, $options = array()) {
397:         if (is_object($name) && $name instanceof ConsoleInputSubcommand) {
398:             $command = $name;
399:             $name = $command->name();
400:         } else {
401:             $defaults = array(
402:                 'name' => $name,
403:                 'help' => '',
404:                 'parser' => null
405:             );
406:             $options = array_merge($defaults, $options);
407:             $command = new ConsoleInputSubcommand($options);
408:         }
409:         $this->_subcommands[$name] = $command;
410:         return $this;
411:     }
412: 
413: /**
414:  * Add multiple subcommands at once.
415:  *
416:  * @param array $commands Array of subcommands.
417:  * @return ConsoleOptionParser $this
418:  */
419:     public function addSubcommands(array $commands) {
420:         foreach ($commands as $name => $params) {
421:             $this->addSubcommand($name, $params);
422:         }
423:         return $this;
424:     }
425: 
426: /**
427:  * Gets the arguments defined in the parser.
428:  *
429:  * @return array Array of argument descriptions
430:  */
431:     public function arguments() {
432:         return $this->_args;
433:     }
434: 
435: /**
436:  * Get the defined options in the parser.
437:  *
438:  * @return array
439:  */
440:     public function options() {
441:         return $this->_options;
442:     }
443: 
444: /**
445:  * Get the array of defined subcommands
446:  *
447:  * @return array
448:  */
449:     public function subcommands() {
450:         return $this->_subcommands;
451:     }
452: 
453: /**
454:  * Parse the argv array into a set of params and args.  If $command is not null
455:  * and $command is equal to a subcommand that has a parser, that parser will be used
456:  * to parse the $argv
457:  *
458:  * @param array $argv Array of args (argv) to parse.
459:  * @param string $command The subcommand to use.  If this parameter is a subcommand, that has a parser,
460:  *    That parser will be used to parse $argv instead.
461:  * @return Array array($params, $args)
462:  * @throws ConsoleException When an invalid parameter is encountered.
463:  */
464:     public function parse($argv, $command = null) {
465:         if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) {
466:             return $this->_subcommands[$command]->parser()->parse($argv);
467:         }
468:         $params = $args = array();
469:         $this->_tokens = $argv;
470:         while (($token = array_shift($this->_tokens)) !== null) {
471:             if (substr($token, 0, 2) == '--') {
472:                 $params = $this->_parseLongOption($token, $params);
473:             } elseif (substr($token, 0, 1) == '-') {
474:                 $params = $this->_parseShortOption($token, $params);
475:             } else {
476:                 $args = $this->_parseArg($token, $args);
477:             }
478:         }
479:         foreach ($this->_args as $i => $arg) {
480:             if ($arg->isRequired() && !isset($args[$i]) && empty($params['help'])) {
481:                 throw new ConsoleException(
482:                     __d('cake_console', 'Missing required arguments. %s is required.', $arg->name())
483:                 );
484:             }
485:         }
486:         foreach ($this->_options as $option) {
487:             $name = $option->name();
488:             $isBoolean = $option->isBoolean();
489:             $default = $option->defaultValue();
490: 
491:             if ($default !== null && !isset($params[$name]) && !$isBoolean) {
492:                 $params[$name] = $default;
493:             }
494:             if ($isBoolean && !isset($params[$name])) {
495:                 $params[$name] = false;
496:             }
497:         }
498:         return array($params, $args);
499:     }
500: 
501: /**
502:  * Gets formatted help for this parser object.
503:  * Generates help text based on the description, options, arguments, subcommands and epilog
504:  * in the parser.
505:  *
506:  * @param string $subcommand If present and a valid subcommand that has a linked parser.
507:  *    That subcommands help will be shown instead.
508:  * @param string $format Define the output format, can be text or xml
509:  * @param integer $width The width to format user content to. Defaults to 72
510:  * @return string Generated help.
511:  */
512:     public function help($subcommand = null, $format = 'text', $width = 72) {
513:         if (
514:             isset($this->_subcommands[$subcommand]) &&
515:             $this->_subcommands[$subcommand]->parser() instanceof self
516:         ) {
517:             $subparser = $this->_subcommands[$subcommand]->parser();
518:             $subparser->command($this->command() . ' ' . $subparser->command());
519:             return $subparser->help(null, $format, $width);
520:         }
521:         $formatter = new HelpFormatter($this);
522:         if ($format == 'text' || $format === true) {
523:             return $formatter->text($width);
524:         } elseif ($format == 'xml') {
525:             return $formatter->xml();
526:         }
527:     }
528: 
529: /**
530:  * Parse the value for a long option out of $this->_tokens.  Will handle
531:  * options with an `=` in them.
532:  *
533:  * @param string $option The option to parse.
534:  * @param array $params The params to append the parsed value into
535:  * @return array Params with $option added in.
536:  */
537:     protected function _parseLongOption($option, $params) {
538:         $name = substr($option, 2);
539:         if (strpos($name, '=') !== false) {
540:             list($name, $value) = explode('=', $name, 2);
541:             array_unshift($this->_tokens, $value);
542:         }
543:         return $this->_parseOption($name, $params);
544:     }
545: 
546: /**
547:  * Parse the value for a short option out of $this->_tokens
548:  * If the $option is a combination of multiple shortcuts like -otf
549:  * they will be shifted onto the token stack and parsed individually.
550:  *
551:  * @param string $option The option to parse.
552:  * @param array $params The params to append the parsed value into
553:  * @return array Params with $option added in.
554:  * @throws ConsoleException When unknown short options are encountered.
555:  */
556:     protected function _parseShortOption($option, $params) {
557:         $key = substr($option, 1);
558:         if (strlen($key) > 1) {
559:             $flags = str_split($key);
560:             $key = $flags[0];
561:             for ($i = 1, $len = count($flags); $i < $len; $i++) {
562:                 array_unshift($this->_tokens, '-' . $flags[$i]);
563:             }
564:         }
565:         if (!isset($this->_shortOptions[$key])) {
566:             throw new ConsoleException(__d('cake_console', 'Unknown short option `%s`', $key));
567:         }
568:         $name = $this->_shortOptions[$key];
569:         return $this->_parseOption($name, $params);
570:     }
571: 
572: /**
573:  * Parse an option by its name index.
574:  *
575:  * @param string $name The name to parse.
576:  * @param array $params The params to append the parsed value into
577:  * @return array Params with $option added in.
578:  * @throws ConsoleException
579:  */
580:     protected function _parseOption($name, $params) {
581:         if (!isset($this->_options[$name])) {
582:             throw new ConsoleException(__d('cake_console', 'Unknown option `%s`', $name));
583:         }
584:         $option = $this->_options[$name];
585:         $isBoolean = $option->isBoolean();
586:         $nextValue = $this->_nextToken();
587:         if (!$isBoolean && !empty($nextValue) && !$this->_optionExists($nextValue)) {
588:             array_shift($this->_tokens);
589:             $value = $nextValue;
590:         } elseif ($isBoolean) {
591:             $value = true;
592:         } else {
593:             $value = $option->defaultValue();
594:         }
595:         if ($option->validChoice($value)) {
596:             $params[$name] = $value;
597:             return $params;
598:         }
599:     }
600: 
601: /**
602:  * Check to see if $name has an option (short/long) defined for it.
603:  *
604:  * @param string $name The name of the option.
605:  * @return boolean
606:  */
607:     protected function _optionExists($name) {
608:         if (substr($name, 0, 2) === '--') {
609:             return isset($this->_options[substr($name, 2)]);
610:         }
611:         if ($name{0} === '-' && $name{1} !== '-') {
612:             return isset($this->_shortOptions[$name{1}]);
613:         }
614:         return false;
615:     }
616: 
617: /**
618:  * Parse an argument, and ensure that the argument doesn't exceed the number of arguments
619:  * and that the argument is a valid choice.
620:  *
621:  * @param string $argument The argument to append
622:  * @param array $args The array of parsed args to append to.
623:  * @return array Args
624:  * @throws ConsoleException
625:  */
626:     protected function _parseArg($argument, $args) {
627:         if (empty($this->_args)) {
628:             array_push($args, $argument);
629:             return $args;
630:         }
631:         $next = count($args);
632:         if (!isset($this->_args[$next])) {
633:             throw new ConsoleException(__d('cake_console', 'Too many arguments.'));
634:         }
635: 
636:         if ($this->_args[$next]->validChoice($argument)) {
637:             array_push($args, $argument);
638:             return $args;
639:         }
640:     }
641: 
642: /**
643:  * Find the next token in the argv set.
644:  *
645:  * @return string next token or ''
646:  */
647:     protected function _nextToken() {
648:         return isset($this->_tokens[0]) ? $this->_tokens[0] : '';
649:     }
650: 
651: }
652: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs