diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..0b05c2754 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,73 @@ +# How to contribute + +CakePHP loves to welcome your contributions. There are several ways to help out: +* Create a ticket in Lighthouse, if you have found a bug +* Write testcases for open bug tickets +* Write patches for open bug/feature tickets, preferably with testcases included +* Contribute to the [documentation](https://github.com/cakephp/docs) + +There are a few guidelines that we need contributors to follow so that we have a +chance of keeping on top of things. + +## Getting Started + +* Make sure you have a [GitHub account](https://github.com/signup/free) +* Submit a ticket for your issue, assuming one does not already exist. + * Clearly describe the issue including steps to reproduce when it is a bug. + * Make sure you fill in the earliest version that you know has the issue. +* Fork the repository on GitHub. + +## Making Changes + +* Create a topic branch from where you want to base your work. + * This is usually the master branch + * Only target release branches if you are certain your fix must be on that + branch + * To quickly create a topic branch based on master; `git branch + master/my_contribution master` then checkout the new branch with `git + checkout master/my_contribution`. Better avoid working directly on the + `master` branch, to avoid conflicts if you pull in updates from origin. +* Make commits of logical units. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Use descriptive commit messages and reference the #ticket number +* Core testcases should continue to pass. You can run tests locally or enable + [travis-ci](https://travis-ci.org/) for your fork, so all tests and codesniffs + will be executed. +* Your work should apply the CakePHP coding standards. + +## Which branch to base the work + +* Bugfix branches will be based on master. +* New features that are backwards compatible will be based on next minor release + branch. +* New features or other non-BC changes will go in the next major release branch. + +## Submitting Changes + +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the repository in the cakephp organization, with the + correct target branch. + +## Testcases and codesniffer + +CakePHP tests requires [PHPUnit](http://www.phpunit.de/manual/current/en/installation.html) +3.5 or higher. To run the testcases locally use the following command: + + ./lib/Cake/Console/cake test core AllTests --stderr + +To run the sniffs for CakePHP coding standards + + phpcs -p --extensions=php --standard=CakePHP ./lib/Cake + +Check the [cakephp-codesniffer](https://github.com/cakephp/cakephp-codesniffer) +repository to setup the CakePHP standard. The README contains installation info +for the sniff and phpcs. + + +# Additional Resources + +* [CakePHP coding standards](http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html) +* [Bug tracker](https://cakephp.lighthouseapp.com/projects/42648-cakephp) +* [General GitHub documentation](http://help.github.com/) +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) +* #cakephp IRC channel on freenode.org diff --git a/lib/Cake/Console/Shell.php b/lib/Cake/Console/Shell.php index 6a0c4360c..94d4e12c3 100644 --- a/lib/Cake/Console/Shell.php +++ b/lib/Cake/Console/Shell.php @@ -22,6 +22,7 @@ App::uses('ConsoleOutput', 'Console'); App::uses('ConsoleInput', 'Console'); App::uses('ConsoleInputSubcommand', 'Console'); App::uses('ConsoleOptionParser', 'Console'); +App::uses('ClassRegistry', 'Utility'); App::uses('File', 'Utility'); App::uses('ClassRegistry', 'Utility'); diff --git a/lib/Cake/Console/Templates/default/actions/controller_actions.ctp b/lib/Cake/Console/Templates/default/actions/controller_actions.ctp index a0c16a5a3..3e7ef27c1 100644 --- a/lib/Cake/Console/Templates/default/actions/controller_actions.ctp +++ b/lib/Cake/Console/Templates/default/actions/controller_actions.ctp @@ -133,7 +133,6 @@ * delete method * * @throws NotFoundException - * @throws MethodNotAllowedException * @param string $id * @return void */ diff --git a/lib/Cake/Core/App.php b/lib/Cake/Core/App.php index ae9942553..e36b10a24 100644 --- a/lib/Cake/Core/App.php +++ b/lib/Cake/Core/App.php @@ -425,6 +425,10 @@ class App { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::objects */ public static function objects($type, $path = null, $cache = true) { + if (empty(self::$_objects) && $cache === true) { + self::$_objects = (array)Cache::read('object_map', '_cake_core_'); + } + $extension = '/\.php$/'; $includeDirectories = false; $name = $type; @@ -451,10 +455,6 @@ class App { $name = $type . str_replace(DS, '', $path); } - if (empty(self::$_objects) && $cache === true) { - self::$_objects = Cache::read('object_map', '_cake_core_'); - } - $cacheLocation = empty($plugin) ? 'app' : $plugin; if ($cache !== true || !isset(self::$_objects[$cacheLocation][$name])) { @@ -768,7 +768,6 @@ class App { */ public static function init() { self::$_map += (array)Cache::read('file_map', '_cake_core_'); - self::$_objects += (array)Cache::read('object_map', '_cake_core_'); register_shutdown_function(array('App', 'shutdown')); } @@ -896,7 +895,6 @@ class App { if (self::$_objectCacheChange) { Cache::write('object_map', self::$_objects, '_cake_core_'); } - self::_checkFatalError(); } diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index d80dda9c7..e23e6cd37 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -221,14 +221,20 @@ class Sqlserver extends DboSource { $fields[$field] = array( 'type' => $this->column($column), 'null' => ($column->Null === 'YES' ? true : false), - 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column->Default), + 'default' => $column->Default, 'length' => $this->length($column), 'key' => ($column->Key == '1') ? 'primary' : false ); if ($fields[$field]['default'] === 'null') { $fields[$field]['default'] = null; - } else { + } + if ($fields[$field]['default'] !== null) { + $fields[$field]['default'] = preg_replace( + "/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", + "$1", + $fields[$field]['default'] + ); $this->value($fields[$field]['default'], $fields[$field]['type']); } diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index 7e5460cc5..244c96903 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -1434,7 +1434,7 @@ class CakeEmail { /** * Render the body of the email. * - * @param string $content Content to render + * @param array $content Content to render * @return array Email body ready to be sent */ protected function _render($content) { diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 4c5fe5699..e2611699d 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -448,7 +448,16 @@ class SqlserverTest extends CakeTestCase { 'Length' => 72, 'Null' => 'NO', 'Size' => '' - ) + ), + (object)array( + 'Default' => null, + 'Field' => 'parent_id', + 'Key' => '0', + 'Type' => 'bigint', + 'Length' => 8, + 'Null' => 'YES', + 'Size' => '0', + ), )); $this->db->executeResultsStack = array($SqlserverTableDescription); $dummyModel = $this->model; @@ -478,9 +487,16 @@ class SqlserverTest extends CakeTestCase { 'default' => '', 'length' => 36, 'key' => 'primary' - ) + ), + 'parent_id' => array( + 'type' => 'biginteger', + 'null' => true, + 'default' => null, + 'length' => 8, + ), ); $this->assertEquals($expected, $result); + $this->assertSame($expected['parent_id'], $result['parent_id']); } /** diff --git a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php index 394195520..7684bdf9c 100644 --- a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php +++ b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php @@ -437,7 +437,7 @@ class CakeTestFixtureTest extends CakeTestCase { $this->insertMulti['fields'] = $fields; $this->insertMulti['values'] = $values; $this->insertMulti['fields_values'] = array(); - foreach($values as $record) { + foreach ($values as $record) { $this->insertMulti['fields_values'][] = array_combine($fields, $record); } return true; @@ -467,18 +467,18 @@ class CakeTestFixtureTest extends CakeTestCase { $this->assertEquals($expected, $this->insertMulti['values']); $expected = array( array( - 'name' => 'Mark Doe', - 'email' => 'mark.doe@email.com', + 'name' => 'Mark Doe', + 'email' => 'mark.doe@email.com', 'age' => null ), array( - 'name' => 'John Doe', - 'email' => 'john.doe@email.com', + 'name' => 'John Doe', + 'email' => 'john.doe@email.com', 'age' => 20 ), array( - 'name' => 'Jane Doe', - 'email' => 'jane.doe@email.com', + 'name' => 'Jane Doe', + 'email' => 'jane.doe@email.com', 'age' => 30 ), ); diff --git a/lib/Cake/Test/Case/TestSuite/CakeTestSuiteDispatcherTest.php b/lib/Cake/Test/Case/TestSuite/CakeTestSuiteDispatcherTest.php deleted file mode 100644 index 9969370f1..000000000 --- a/lib/Cake/Test/Case/TestSuite/CakeTestSuiteDispatcherTest.php +++ /dev/null @@ -1,38 +0,0 @@ -vendors = App::path('vendors'); - $this->includePath = ini_get('include_path'); - } - - public function tearDown() { - App::build(array('Vendor' => $this->vendors), App::RESET); - ini_set('include_path', $this->includePath); - } - - protected function clearPaths() { - App::build(array('Vendor' => array('junk')), App::RESET); - ini_set('include_path', 'junk'); - } - - public function testLoadTestFramework() { - $dispatcher = new CakeTestSuiteDispatcher(); - - $this->assertTrue($dispatcher->loadTestFramework()); - - $this->clearPaths(); - - $exception = null; - - try { - $dispatcher->loadTestFramework(); - } catch (Exception $ex) { - $exception = $ex; - } - - $this->assertEquals(get_class($exception), "PHPUnit_Framework_Error_Warning"); - } - -} diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index 7acf57927..e3684ef81 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -2165,6 +2165,10 @@ class ValidationTest extends CakeTestCase { $this->assertFalse(Validation::postal('BAA 0ABC', null, 'ca')); $this->assertFalse(Validation::postal('B2A AABC', null, 'ca')); $this->assertFalse(Validation::postal('B2A 2AB', null, 'ca')); + $this->assertFalse(Validation::postal('K1A 1D1', null, 'ca')); + $this->assertFalse(Validation::postal('K1O 1Q1', null, 'ca')); + $this->assertFalse(Validation::postal('A1A 1U1', null, 'ca')); + $this->assertFalse(Validation::postal('A1F 1B1', null, 'ca')); $this->assertTrue(Validation::postal('X0A 0A2', null, 'ca')); $this->assertTrue(Validation::postal('G4V 4C3', null, 'ca')); diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index e1cb97338..2c81507e9 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -540,6 +540,41 @@ XML; } } +/** + * Test that there are not unterminated errors when building xml + * + * @return void + */ + public function testFromArrayUnterminatedError() { + $data = array( + 'product_ID' => 'GENERT-DL', + 'deeplink' => 'http://example.com/deep', + 'image_URL' => 'http://example.com/image', + 'thumbnail_image_URL' => 'http://example.com/thumb', + 'brand' => 'Malte Lange & Co', + 'availability' => 'in stock', + 'authors' => array( + 'author' => array('Malte Lange & Co') + ) + ); + $xml = Xml::fromArray(array('products' => $data), 'tags'); + $expected = << + + GENERT-DL + http://example.com/deep + http://example.com/image + http://example.com/thumb + Malte Lange & Co + in stock + + Malte Lange & Co + + +XML; + $this->assertXmlStringEqualsXmlString($expected, $xml->asXML()); + } + /** * testToArray method * diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index b95f21ce5..4988cd39f 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -2276,6 +2276,33 @@ class FormHelperTest extends CakeTestCase { $this->assertContains('', $result); } +/** + * Test interval & timeFormat = 12 + * + * @return void + */ + public function testInputTimeWithIntervalAnd12HourFormat() { + $result = $this->Form->input('Model.start_time', array( + 'type' => 'time', + 'timeFormat' => 12, + 'interval' => 5, + 'selected' => array('hour' => '4', 'min' => '30', 'meridian' => 'pm') + )); + $this->assertContains('', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); + + $result = $this->Form->input('Model.start_time', array( + 'type' => 'time', + 'timeFormat' => '12', + 'interval' => 5, + 'selected' => '2013-04-19 16:30:00' + )); + $this->assertContains('', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); + } + /** * test form->input() with datetime, date and time types * diff --git a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php index 86dfd3895..636f13b57 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php +++ b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php @@ -137,15 +137,18 @@ class CakeTestSuiteDispatcher { * @return boolean true if found, false otherwise */ public function loadTestFramework() { + if (class_exists('PHPUnit_Framework_TestCase')) { + return true; + } foreach (App::path('vendors') as $vendor) { - $vendor = rtrim($vendor, DS); + $vendor = rtrim($vendor, DS); if (is_dir($vendor . DS . 'PHPUnit')) { ini_set('include_path', $vendor . PATH_SEPARATOR . ini_get('include_path')); break; } } - - return (include('PHPUnit' . DS . 'Autoload.php')) !== false; + include 'PHPUnit' . DS . 'Autoload.php'; + return class_exists('PHPUnit_Framework_TestCase'); } /** diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 342a6fbcc..7b5777d76 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -652,7 +652,9 @@ class Validation { $regex = '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i'; break; case 'ca': - $regex = '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z] [0-9][A-Z][0-9]\\b\\z/i'; + $district = '[ABCEGHJKLMNPRSTVYX]'; + $letters = '[ABCEGHJKLMNPRSTVWXYZ]'; + $regex = "/\\A\\b{$district}[0-9]{$letters} [0-9]{$letters}[0-9]\\b\\z/i"; break; case 'it': case 'de': @@ -784,7 +786,7 @@ class Validation { * @return boolean Success */ public static function uuid($check) { - $regex = '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i'; + $regex = '/^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[1-5][a-fA-F0-9]{3}-[89aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$/'; return self::_check($check, $regex); } diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index b7e404452..860121fe6 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -305,10 +305,9 @@ class Xml { $childValue = (string)$value; } + $child = $dom->createElement($key); if ($childValue) { - $child = $dom->createElement($key, $childValue); - } else { - $child = $dom->createElement($key); + $child->appendChild($dom->createTextNode($childValue)); } if ($childNS) { $child->setAttribute('xmlns', $childNS); diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 0733cfd90..e7374a40f 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -2387,7 +2387,8 @@ class FormHelper extends AppHelper { } $change = (round($min * (1 / $interval)) * $interval) - $min; $current->modify($change > 0 ? "+$change minutes" : "$change minutes"); - $newTime = explode(' ', $current->format('Y m d H i a')); + $format = ($timeFormat === '12') ? 'Y m d h i a' : 'Y m d H i a'; + $newTime = explode(' ', $current->format($format)); list($year, $month, $day, $hour, $min, $meridian) = $newTime; } @@ -2507,14 +2508,14 @@ class FormHelper extends AppHelper { if (!empty($timeFormat)) { $time = explode(':', $days[1]); - if ($time[0] >= '12' && $timeFormat === '12') { + if ($time[0] >= 12 && $timeFormat == 12) { $meridian = 'pm'; - } elseif ($time[0] === '00' && $timeFormat === '12') { + } elseif ($time[0] === '00' && $timeFormat == 12) { $time[0] = 12; } elseif ($time[0] >= 12) { $meridian = 'pm'; } - if ($time[0] == 0 && $timeFormat === '12') { + if ($time[0] == 0 && $timeFormat == 12) { $time[0] = 12; } $hour = $min = null; diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index f3de05497..ff492b839 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -560,14 +560,13 @@ class View extends Object { //@codingStandardsIgnoreStart @unlink($filename); //@codingStandardsIgnoreEnd - unset ($out); + unset($out); return false; } else { if ($this->layout === 'xml') { header('Content-type: text/xml'); } - $commentLength = strlen(''); - return substr($out, $commentLength); + return substr($out, strlen($match[0])); } } }