Giter Site home page Giter Site logo

phalcon / phalcon Goto Github PK

View Code? Open in Web Editor NEW
197.0 27.0 44.0 6.3 MB

[WIP] Phalcon Framework. Work will continue after release of v5.0

Home Page: https://phalcon.io

License: MIT License

PHP 99.66% CSS 0.01% JavaScript 0.01% Shell 0.16% Dockerfile 0.12% HTML 0.01% PLpgSQL 0.06%
phalcon phalcon-framework hacktoberfest php

phalcon's Introduction

phalcon

Phalcon Framework

PDS Skeleton Scrutinizer Code Quality Code Coverage codecov

Implemented

Standard Description
PSR-3 Phalcon\Logger\Logger
PSR-4 Phalcon\Autoloader
PSR-12 Coding Standard
PSR-13 Phalcon\Html\Tag\Link
PSR-16 Phalcon\Cache\Cache

To do

Standard Description
PSR-6 Caching Interface
PSR-7 Phalcon\Http\Message
PSR-11 Container Interface
PSR-14 Event Dispatcher
PSR-15 HTTP Handlers
PSR-17 Phalcon\Http\Message
PSR-18 HTTP Client

Remaining refactoring - alignment with cphalcon (v5)

  • Annotations
  • DataMapper
  • Db
  • Dispatcher
  • Mvc
  • Paginator
  • Url

phalcon's People

Contributors

arhell avatar dependabot[bot] avatar jeckerson avatar jeijei4 avatar niden avatar tacxou avatar zsilbi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phalcon's Issues

[NFR] Volt output filters

Description
Since Volt is by default caching generated php templates, it might be useful to apply even time-consumpting filters on the output created at this stage. Filters could for example minify the HTML source code or do any further optimization.

Usage Example

use \Phalcon\View\Engine\Volt\Filters\HTMLMinify,
    \Phalcon\Mvc\View\Engine\Volt;

[...]

$di->set('view', function() {
        $view = new View();
        $view->setViewsDir('../app/views/');
        $view->registerEngines(array(
            '.volt' => function($view, $di) {
                $volt = new Volt($view, $di);
                $volt->addFilter(new HTMLMinify());

                return $volt;
            }
            ));
        return $view;
    });

Framework Integration
The filter integration would match the filter integration of the Phalcon\Assets\Manager class. All filters should implement FilterInterface.

Suggested Additional Classes, Interfaces, Methods and Member Variables

  • \Phalcon\Mvc\View\Engine\Volt
class Volt extends Engine implements InjectionAwareInterface, 
EventsAwareInterface, EngineInterface
{
    /**
     * Filters
     *
     * @var array|null
     * @access protected
    */
    protected $_filters = null;

    /**
     * Apply filter on output
     *
     * @param FilterInterface $filter
    */
    public function addFilter(FilterInterface $filter) {}

    /**
     * Remove applied filter
     *
     * @param FilterInterface $filter
    */
    public function removeFilter(FilterInterface $filter) {}

    /**
     * Get applied filters
     *
     * @return array
    */
    public function getFilters() {}
}
  • \Phalcon\Mvc\View\Engine\Volt\FilterInterface
interface FilterInterface
{
    /**
     * Filters $content and returns the output
     *
     * @param string $content
     * @return string
     * @throws \Phalcon\Mvc\View\Exception
    */
    public function filter($content);
}
  • \Phalcon\Mvc\View\Engine\Volt\Filters\HTMLMinify
class HTMLMinify implements FilterInterface
{
    /**
     * Applies php_strip_whitespace and minifies the HTML code around
     *
     * @param string $content
     * @return string
     * @throws \Phalcon\Mvc\View\Exception
    */
    public function filter($content);
}

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

PresenceOf and Regex validators

Hello again =))
It's a new feature request for 4.x.x again =))

The current behavior of the Regex and other validators that work with strings.:
When passing an array to the Regex validator, an array to string conversion error is thrown.

The new behavior of the Regex and other validators that work with strings:
When passing an array to the Regex validator must be returned the false value.
This should be achieved with an additional option to the validator. For example: 'failIfPassedNotString

UPD: Do not take PresenceOf in your head, wrote by accident.

Thnx.

url get() function enhancement request

$current_url = 'http://example.com?page=1';

echo $url->get($current_url, ['page' => 5]);

My expectation must be:

http://example.com?page=5

But the returned url is:

http://example.com?page=1&page=5

[NFR] add aspect ratios validation for Validator/Files

Right now we can set minResolution and maxResolution options.

But in responsive design is more useful have images with same aspect ratios.
For example if you show list of cards with images.

the code could be looks like that:

'aspectRatio' => [
  'file' => '16by9' // or 16x9
]

\Phalcon\Mvc\Model::find/findFirst/findBy() searches within wrong schema/database

Hi,

I have a model-class in which I can set the source and database/schema dynamically. So I am able to switch the schema without having to create a separate definition for each model.

Maschine-Details

  • Phalcon version: 3.1.1
  • PHP Version: 7.0.17
  • Operating System: Windows
  • Installation type: php-extension downloaded from phalconphp.com
  • Zephir version: Version 0.9.6a-dev-3a72ba9bb5
  • Server: Apache
  • DatabaseManagementSystem: MySQL 5.6.34

Expected Behavior

I get two different results - one from database 1 and one from database 2.

Actual Behavior

Phalcon searches within first database. After changing the schema, phalcon tries to get informations about database 2, but after that it still searches within database 1!

Code

SQL

CREATE DATABASE `test_1`;
USE `test_1`;
CREATE TABLE `test_table` (
  `id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `text` VARCHAR(255) NOT NULL DEFAULT 'blah',
PRIMARY KEY (id));
INSERT INTO `test_1`.`test_table` (`id`) VALUES (1); 

CREATE DATABASE `test_2`;
USE `test_2`;
CREATE TABLE `test_table` (
  `id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `text` VARCHAR(255) NOT NULL DEFAULT 'blubb',
PRIMARY KEY (id));
INSERT INTO `test_2`.`test_table` (`id`) VALUES (1); 

PHP

namespace Mapserver\Models;

class TestModel extends \Phalcon\Mvc\Model
{
    public static $database = '';
    public static $source = 'test_table';

    public function initialize()
    {
        $this->setConnectionService('db');
        $this->setSchema(static::$database);
        $this->setSource(static::$source);
    }

    public function getSource()
    {
        return static::$source;
    }

    public function getSchema()
    {
        return static::$database;
    }

}

// I set the database/schema: 
\Mapserver\Models\TestModel::$database = 'test_1';

// I try to find an object which is in database 1
$obj_1 = \Mapserver\Models\TestModel::findFirstById(1);
var_dump($obj_1->getSchema() );
var_dump($obj_1->toArray());
//Query is o.k, all works fine.

// I change the database/schema
\Mapserver\Models\TestModel::$database = 'test_2';

// I try to find an object which is in database 2
$obj_2 = \Mapserver\Models\TestModel::findFirstById(1);
var_dump($obj_2->getSchema() );
var_dump($obj_2->toArray());

Results

Note, that $obj_2->getSchema() gives the correct database!

string(6) "test_1"
array(2) {
  ["id"]=>
  string(1) "1"
  ["text"]=>
  string(4) "blah"
}

string(6) "test_2"
array(2) {
  ["id"]=>
  string(1) "1"
  ["text"]=>
  string(4) "blah"
}

Take a look at the query-log:

[11:53:34] SELECT IF(COUNT(*) > 0, 1, 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`= 'test_table' AND `TABLE_SCHEMA` = 'test_1'
[11:53:34] DESCRIBE `test_1`.`test_table`
[11:53:34] SELECT `test_table`.`id`, `test_table`.`text` FROM `test_1`.`test_table` WHERE `test_table`.`id` = :0 [1]
[11:53:34] SELECT IF(COUNT(*) > 0, 1, 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`= 'test_table' AND `TABLE_SCHEMA` = 'test_2'
[11:53:34] DESCRIBE `test_2`.`test_table`
[11:53:34] SELECT `test_table`.`id`, `test_table`.`text` FROM `test_1`.`test_table` WHERE `test_table`.`id` = :0 [1]

After changing the schema it queries: "DESCRIBE test_2.test_table"
but then it does: "SELECT ... FROM test_1.test_table WHERE ..."

Issue may be related to these old ones: phalcon/cphalcon#3018 and phalcon/cphalcon#2415

generate URL for route with hostName

When you generate a URL for a route for which the specified host, you can add the host in the URL.

$router->add('/login', array(
    'module' => 'account',
    'controller' => 'auth',
    'action' => 'login'
))->setHostName('account.company.com')
->setName('account_login');

Now we get this URL

$url->get(array('for' => 'account_login')); // '/login'

But I would like to get the URL with the desired subdomain

$url->get(array('for' => 'account_login')); // 'account.company.com/login'

volt strings do not know about escape sequences

Expected and Actual Behavior

In Volt the string 'Let\'s Encrypt' should be handled like "Let's Encrypt".

However, using 'Let\'s Encrypt' causes a parser failure. This is especially problematic with strings that have both single and double quotes as there is no way to have them in one contained string.

This is suboptimal for translation efforts, where we need contained strings with longer text to be embedded into the templates in the first place.

Parse error: syntax error, unexpected 'to' (T_STRING), expecting ',' or ')'

Details

  • Phalcon version: 3.1.2
  • PHP Version: 7.0
  • Operating System: FreeBSD
  • Installation type: package manager
  • Server: Lighttpd

[NFR] Volt with "literal" or "verbatim" tag

There should be a tag like the Smarty {literal} tag http://www.smarty.net/docsv2/en/language.function.literal

witch allows us to create a block of content that will be not parsed by the volt compiler and outputed as is!

This is useful if you want to put xml headers into a volt template like this:

<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type='text/xsl' href='/s/sitemap.xsl'?>

without having the <?xml breaking the compiled template. Other use cases is if you really want to have in you html things like "{{ I relly want {{ }} in html }}"

This cold be achieved with something like

{% literal %}
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type='text/xsl' href='/s/sitemap.xsl'?>
{% endliteral %}

{% literal %}
<b>{{</b> I relly want {{ }} in html <b>}}</b>
{% endliteral %}

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

[NFR] Add proper support to \Phalcon\Form for Radio button

Expected and Actual Behavior

In a normal html radio button, you can and normally use the same input name to select an option value:

i.e.:

<form>
  <input type="radio" id="gender_male" name="gender" value="male"/>
  <label for="gender_male">Male</label>
  <input type="radio" id="gender_female" name="gender" value="female"/>
  <label for="gender_female">Female</label>
</form>

But since Phalcon Forms use the component "name" and not "id" identify them you can't use them.

Details

  • Phalcon version: 3.x.x
  • PHP Version: all

One way to add support would be use ids and not names to identify components in Phalcon\Form

Thanks!
Federico.

Implement Argon2 for password hashing

With the release of PHP 7.2, Argon2 has been available for use for password hashing under the PASSWORD_ARGON2I constant. The upcoming release of PHP 7.3 will build on this and enable PASSWORD_ARGON2D and PASSWORD_ARGON2ID.

Unlike BCRYPT, ARGON2 is resistant to GPU based attacks as it accesses the memory array in a password dependent order, which reduces the possibility of timeโ€“memory trade-off attack.

https://en.wikipedia.org/wiki/Argon2
https://password-hashing.net
https://github.com/P-H-C/phc-winner-argon2
http://php.net/manual/en/function.password-hash.php

Queue Service

In Phalcon v4 we removed Beanstalkd (#13364, phalcon/cphalcon#13850) since the project is no longer supported.

This issue serves as a discussion and design board for potentially a new Queue adapter that can be used for future integrations with popular queue systems.

https://github.com/queue-interop/queue-interop is a good candidate

Alternatively, we can leave the queue system completely out of Phalcon and use implementations from the incubator or custom built ones from developers based on the needs of their applications.

Parametric Questions in Command Line Mode

In command line mode
The code is as follows :( fragment )

$arguments = [];
$arguments["task"] = 'Test';
$arguments["action"] = 'index';
$arguments["params"]=['a','b'];
$console->handle($arguments);
class MainTask extends Task
{
    public function mainAction()
    {
        echo var_dump(func_get_args());
    }
}

output

array(2) {
  [0]=>
  array(2) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
  }
  [1]=>
  array(0) {
  }
}

I read the source code and found "https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/dispatcher.zep#L886".
I read PHPDoc and my predicted output is:

  array(2) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
  }
 

It's different from what I think. Why?

Details

  • Phalcon version: 3.4.2
  • PHP Version: 7.2.11

[NFR]: Review logic of "Load services from config"

Is your feature request related to a problem? Please describe.
https://docs.phalcon.io/4.0/en/di#load-from-config

Load services from config is pretty confusing in some cases, especially, when you want to wrap common approach service providers classes and just load them via $di->loadFromPhp().

Describe the solution you'd like
In my opition, current approach of loading the services, need to be review and rewritten or removed...

Example how would be nice to do, but you can't:

$di->loadFromPhp('services.php');

photo_2019-11-30_20-14-37

[BUG] Phalcon can't handle models with AI PK as only column

Expected and Actual Behavior

I want to have a model which serves as some sort of base reference, and it has only one column: auto incremented PK.

Trying to save such model results in exception being thrown by Phalcon\Db\Adapter line 319

  • Phalcon version: 3.4.0
  • PHP Version: PHP 7+
  • Operating System: macOS
  • Other related info (Database, table schema): MySQL 8.0.12

Temp workaround: add nullable dummy field

[NFR] ReusableInterface

Context

Days ago I was working on a problem with the new instances returned by methods that call related elements in models in this publication

I have found a simple way to solve this problem by implementing a simple interface in the classes of models that we want to reuse globally in our applications

My Propose

  1. Change _reusable protected property to static in Model Manager _reusable property must remain as it is
  2. Create a new interface ReusableInterface
namespace Phalcon\Mvc\Model;

interface ReusableInterface {
    public function getUniqueKey();
}
  1. Modify how the model manager obtains the unique key of the models. Source
if reusable {
    if referencedModel instanceof ReusableInterface {
       let uniqueKey = referencedModel->getUniqueKey();
    } else {
       let uniqueKey = unique_key(referencedModel, arguments);
    }

    let records = this->getReusableRecords(referencedModel, uniqueKey);
    if typeof records == "array" || typeof records == "object" {
        return records;
    }
}

Usage

class Invoice extends \Phalcon\Mvc\Model implements \Phalcon\Mvc\Model\ReusableInterface {
    const COL_ID = 'id';
    const COL_DATE = 'date';
    const REL_ITEMS = 'relItems';

    private $id;
    private $date;

    public function initialize() {
        $this->setSource('invoices');

        $this->hasMany(
            self::COL_ID,
            Item::class,
            Item::COL_INVOICE_ID,
            [
                'alias'      => self::REL_ITEMS,
                'reusable'   => true,
            ]
        );
    }

    public function getUniqueKey() {
        return __CLASS__ . ':' . self::COL_ID . ':' . $this->getId();
    }

    public function getItems(): Simple 
    {
        return $this->{self::REL_ITEMS};
    }

    public function setItems(array $items = []) : self
    {
        $this->{self::REL_ITEMS} = $items;
        return $this;
    }

    // setters getters
}

class Item extends \Phalcon\Mvc\Model implements \Phalcon\Mvc\Model\ReusableInterface {
    const COL_ID = 'id';
    const COL_NAME = 'name';
    const COL_PRICE = 'price';
    const COL_INVOICE_ID = 'invoiceId';
    const REL_INVOICE = 'relInvoice';

    private $id;
    private $name;
    private $price;
    private $invoiceId;

    public function initialize() {
        $this->setSource('invoices_items');

        $this->belongsTo(
            self::COL_INVOICE_ID,
            Invoice::class,
            Invoice::COL_ID,
            [
                'alias'      => self::REL_INVOICE,
                'foreignKey' => [
                    'allowNulls' => false,
                    'message'    => 'Hey! where is my invoice?',
                ],
                'reusable'   => true,
            ]
        );
    }

    public function getUniqueKey() {
        return __CLASS__ . ':' . self::COL_ID . ':' . $this->getId();
    }

    public function getInvoice(): ?Invoice 
    {
        return $this->{self::REL_INVOICE} ?: null;
    }

    public function setInvoice(Invoice $invoice) : self
    {
        $this->{self::REL_INVOICE} = $invoice;
        return $this;
    }

    // setters getters
}

// After
$invoice = (new Invoice())->findFirst(1234);
var_dump(spl_object_hash($invoice)); // 00000000546e8820000000005fdc6415

var_dump(spl_object_hash($invoice->getItems()->getFirst()->getInvoice())); // 00000000546efff0000000005fdc6415


// Before
$invoice = (new Invoice())->findFirst(1234);
var_dump(spl_object_hash($invoice)); // 00000000546e8820000000005fdc6415

var_dump(spl_object_hash($invoice->getItems()->getFirst()->getInvoice())); // 00000000546e8820000000005fdc6415

Well I hope your comments on the matter. I hope I have been explicit enough in my idea

Rgds

[BUG]: PHQL; Error on UPDATE SET col = expression with placeholder

Describe the bug
When using an expression with a placeholder after SET in an UPDATE query, I get the following error: Invalid parameter number: mixed named and positional parameters. Probably because of the SELECT query that is made before the UPDATE query.
A pretty common scenario would be: SET col = col + some_number.

To Reproduce

<?php
class Objects extends \Phalcon\Mvc\Model
{
	public $obj_id;
	public $obj_name;
	public $obj_type;

	public function initialize() {
		$this->useDynamicUpdate(true);
		// $this->skipAttributes(['obj_name']);
	}
}

$di = new \Phalcon\Di\FactoryDefault;

$di->set('db', function() {
	$db = new \Phalcon\Db\Adapter\Pdo\Mysql([
		'dbname'   => 'test',
		'username' => 'test',
		'password' => 'test',
	]);

	$eventsManager = new \Phalcon\Events\Manager();
	$eventsManager->attach('db:afterQuery', function($ev, $db) {
		echo $db->getSQLStatement() . "\n";
	});
	$db->setEventsManager($eventsManager);
	return $db;
});

try {
	/*\Phalcon\Mvc\Model::setup([
		'events' => false,
		'virtualForeignKeys' => false,
	]);*/

	$app = new \Phalcon\Mvc\Application($di);
	$app->modelsManager->executeQuery(
		'UPDATE Objects SET obj_type = obj_type + :obj_type: WHERE obj_id = 1', // err
		// 'SET obj_type = obj_type + 2', // ok
		// 'SET obj_type = :obj_type:', // ok
		[
			'obj_type' => 2
		]
	);
} catch ( Exception $ex ) {
	print_r($ex->getMessage() . "\n");
}

Expected behavior
To be able to use placeholders in expressions after SET.

Details

  • Phalcon version: 4.0.0-rc.3, Nov 16 2019 18:04:58, Zephir Version 0.12.11-8b9430c
  • PHP Version: PHP 7.3.12 (cli) (built: Nov 19 2019 13:57:43) ( NTS MSVC15 (Visual C++ 2017) x64 )
  • Operating System: Win 10 x64
  • MySQL: 5.7.28
  • Server: Nginx
  • Other related info (Database, table schema):
CREATE DATABASE IF NOT EXISTS `test`;
USE `test`;

CREATE TABLE IF NOT EXISTS `objects` (
 `obj_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `obj_name` varchar(20) NOT NULL,
 `obj_type` tinyint(3) unsigned NOT NULL,
 PRIMARY KEY (`obj_id`)
) ENGINE=InnoDB;

INSERT INTO `objects` (`obj_id`, `obj_name`, `obj_type`) VALUES (1, 'test1', 1);

Checkbox binding

Currently binding entity to form with unmarked checkboxes won't set corresponding fields to anything. I use this code to fix this right now:

if ($this->request->isPost() && $form->isValid($_POST, $customer)) {
    $customer->ours = isset($_POST['ours']) ? 1 : 0;
    $customer->save();
}

Do I miss something?

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Remove Di::getDefault() from internal calls

For all components in the framework that implement the InjectionAware interface this->get(service) should be used, without any calls to Di::getDefault()

It should be the application's responsibility to set the DI container in the said component properly.

Also any calls to getShared should be replaced with get() because if the component is registered as shared get() returns the shared instance

[Memcached] Multiple memcached instances conflict

Hello,
I've set up a multi-site, the code is all shared between both sites.
I Just change the db and caching port of the sites.

The problem is that the 2nd Application is getting keys from the 1st application.
Maybe phalcon is caching the config array in someplace we don't have access?

  • In Nginx I create a variable telling me which application i should run (it checks domain in nginx)
    So i set in index.php:
    if (isset($_SERVER['Website2']))
        $config = include __DIR__ . "/../app/config/config_2.php";
    else 
        $config = include __DIR__ . "/../app/config/config.php";

this is the configuration file that differs each website.. It does work as planned because all data that is not cached comes without problems.

In services.php I set:

$di->setShared('modelsCache', function () use ($config) {
    $frontCache = new \Phalcon\Cache\Frontend\Data(array("lifetime" => 600));
$cache = new Phalcon\Cache\Backend\Libmemcached($frontCache, array(
     'servers' => array(
         array('host' => $config->memcache->host,
               'port' => $config->memcache->port,
               'weight' => 1
           ),
     ),
     'client' => array(Memcached::OPT_HASH => Memcached::HASH_MD5     )
 ));

    return $cache;
});
  • When i try to get content from the Models in Website2, it return content from Website1. When i do a dump of the result, it shows me that it got from the other server (also i check in memcached and it does not have the key set).

  • Is there a hidden place where i should set this (and it's not in documentation so it would be a documentation bug)?

  • Is it a bug per se?

Thanks

Alan

Details

  • Phalcon version: (3.2.0)
  • PHP Version: (5.6.30)
  • Operating System: CentOS
  • Installation type: Compiling from source
  • Zephir version (if any):
  • Server: Nginx + PHP FPM
  • Other related info (Galera Mysql + Memcached (2 instances in same server different ports):

[RFC] Require Timestampable model behavior only take one field

Something that I found in phalcon/cphalcon#14027 is that the Timestampable model behavior has the option to pass a string or an array of fields:

https://github.com/phalcon/cphalcon/blob/7d8ffd16f6f44bf28d0d700b34cfd6121494023a/phalcon/Mvc/Model/Behavior/Timestampable.zep#L80

If you pass an array, those fields will be updated with the same timestamp and in the same format:

+-----+---------------------+---------------------+
| ID  | Timestamp1          | Timestamp2          |
|-----|---------------------|---------------------|
| 123 | 2019-05-04 08:55:23 | 2019-05-04 08:55:23 |
+-----+---------------------+---------------------+

Why would anyone need to legitimately use this? Is it safe to remove passing as an array?

[NFR]: Forms should transform "" to NULL unless explicitly told not to

Database

CREATE TABLE `accommodation` (
  ...
  `website` VARCHAR(60) DEFAULT NULL
  ...

Controller

// Request a transaction
$transaction = $this->transactionManager->get();

$accommodation = new Accommodation();
$accommodation->setTransaction($transaction);

...

$accommodationForm->bind(
    $_POST,
    $accommodation,
    [
        ...
        'website',
        ...
    ]
);

if ($accommodation->create() == false) {
    $transaction->rollback(__('Error has occured while creating accommodation'), $accommodation);
}

...

// no errors, commit changes
$transaction->commit();  

Form

// website input
$website = new Text('website');
$website->setLabel(__('Website'));
/*$website->addValidator(
    new Url(
        [
            "message" => __('Please enter valid website address')
        ]
    )
);*/
$this->add($website);

Expected behavior

DB field should be null when nothing was entered in the field

Actual behavior

DB field is empty string

Details

  • Phalcon version: 3.4.0
  • PHP Version: 7.2.8
  • Operating System: macOS
  • Installation type: Compiling from source
  • Server: Nginx
  • Database: MySQL 8.0.12

[NFR] Allow \Phalcon\Mvc\View\Engine\Volt to accept Phalcon\Mvc\View\Engine\Volt\Compiler in the constructor

If would be nice if \Phalcon\Mvc\View\Engine\Volt could accept a Compiler in the constructor. This would be helpful when using the CLI interface to precompile every Volt view. As it currently stands it doesn't appear that I can use the Volt Compiler in a DI container and so I need to require() Volt configuration code in each place that I want to use the compiler.

Examples of what I need to currently do:

Web:

$compiler = $volt->getCompiler();
require($config->application->phalconDir . '/config/volt_compiler.php');

CLI:

$compiler = new Compiler();
require($config->application->phalconDir . '/config/volt_compiler.php');

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Many to Many behaviour

The way many to many behaves, does not appear to be very intuitive, and is not nice to use. for example

$tag1 = \Tag::findFirst('id=1');
$tag2 = \Tag::findFirst('id=2');
$tag3 = \Tag::findFirst('id=3');

// create a post
$post = new \Post();
$post->tags = array($tag1, $tag2);
$post->save();

// edit a post
$post = \Post::findFirst('id=1'); // the same post that was created earlier
$post->tags = array($tag1, $tag3);
$post->save();

Will tend to mean the post ends up with all three tags, possibly even with an extra copy of tag1, rather than just 2 as might be expected, and how Doctrine works.

Is this a bug, or intended behaviour?

Some official word on this would be great, seems to attract a few people asking about it http://forum.phalconphp.com/discussion/2190/many-to-many-expected-behaviour

thanks :)

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

[NFR]: Need the lock for redis session

Hi guys,

I think we need to implement a lock for the Redis session.
Without it may cause some unexpected bugs, like login failed,
because of the login request may take a little longer time to init some user data,
but the next request has arrived at the time.

There may have many ways to deal with this, using phpredis's session locking
phpredis session locking

or just add a new method to the session manager to give the chance for some check.
Exp:

    public function hasRetry(string $key): bool
    {
        $has = false;

        for ($i = 0; $i < 100; $i++)
        {
            $has = $this->has($key);

            if ($has) { break; }

            usleep(50000);
        }

        return $has;
    }

[NFR] ORM: add the ability to return a custom Phalcon\Mvc\Model\Row class instance

Overview

Similar to issue phalcon/cphalcon#12166 it would be great if this could be taken a step further so that a custom Phalcon\Mvc\Model\Row can be returned when using Query Builder for more complex queries.

See original post on forum.

Reason for NFR & Example Usage / Syntax

At the moment, if you use Model::find() methods and don't specify columns you are returned an instance of Model. If you use joins and/or custom columns, the class of each row becomes Phalcon\Mvc\Model\Row. If we could override this class, it would be possible to have re-usable utility methods to keep things DRY that would be usable in your View, Controller etc.

Looking at the source, it doesn't seem possible to override the use of Phalcon\Mvc\Model\Row.

Suggested Syntax

In your model class, similar to phalcon/cphalcon#12166:

namespace Models;
class Users extends \Phalcon\Mvc\Model {
    public $id;
    public $first_name;
    public $last_name;

    public function getResultsetRowClass() {
        return 'Resultsets\Row\User';
    }
}
namespace Resultsets\Row;
class User extends Phalcon\Mvc\Model\Row {
    public function getFullName() {
        return sprintf('%s %s', $this->first_name, $this->last_name);
    }
}

You could even put the getFullName() method in a trait so that the logic can be included in your model as well to keep things DRY.

Then in your view you could do something like:

{% for user in users %}
    {{ user.id }}: {{ user.getFullName() }} 
{% endfor %}

An alternative would be to add this when you're executing a built query:

$data = $queryBuilder->getQuery()->setResultsetRowClass('Resultsets\Row\User')->execute();

I'm by no means an expert but I think this might be more straight forward than adding to the model actually. We could have setResultsetRowClass / getResultsetRowClass methods added into Phalcon\Mvc\Model\Query which would be used on line 2816:

let resultObjectClass = this->getResultsetRowClass();
let resultObject = new {resultObjectClass}();

Details

  • Phalcon version: 3.4.0
  • PHP Version: 7.2.11
  • Operating System: macOS
  • Installation type: HomeBrew
  • Zephir version (if any): n/a
  • Server: Apache
  • Other related info (Database, table schema): MySQL 5.7

[NFR] HTML OPTION attributes by Phalcon\Tag::select()

For now it's impossible to set attributes for drop-down list options using Phalcon\Tag::select()
I propose to implement it this way

$this->tag->select([
    'robotId',
    Robots::find(),
    'using' => ['id', 'name'],
    // parameter 'options' is array of attributes which must be set for OPTIONs
    'options' => [
        // class "favorite" will be set for OPTIONs with id 1 and 3
        'class' => ['favorite', [1, 3]],
        // "disabled" attribute will be set for destroyed robots
        'disabled' => function($robot){return $robot->is_destroyed ? 'disabled' : false;},
        // dir="rtl" will be set for all OPTIONs *
        'dir' => 'rtl', 
    ],
]);

* actually I don't know the reason to set the same attribute for every OPTION

Output:

<select name="robotId">
    <option value="1" dir="rtl" class="favorite">Robocop</option>
    <option value="2" dir="rtl" disabled="disabled">Unicron</option>
    <option value="3" dir="rtl" class="favorite">Bender</option>
    <option value="4" dir="rtl">R2-D2</option>
</select>

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

[NFR]: Event Manager Redesign/Updates

This relates to phalcon/cphalcon#14675

The events manager stops execution if one event returns false. This is true only if one listener is attached to the particular event.

The events manager needs to be updated to allow:

  • Option to stop execution for a particular event to all listeners attached to the particular event
  • Option to stop execution for all events after one listener returns false

We can use stop() to achieve this but a more configurable alternative would be nice.

Controller __get() and getDI()->get() return different results.

It seems that the service returned using magic methods inside controllers are cached.

If I replace a default service inside a module, it won't be referenced properly inside controllers.

public function defaultAction($campaign, $pixel) {
        $di = \Phalcon\DI\FactoryDefault::getDefault();
        var_dump($di === $this->getDI(), $this->getDI()->get('request') === $this->request);
}

returns

bool(true)
bool(false)

This doesn't make sense: the internal DI is clearly the same as DI that is statically resolved, so I'm not sure where $this->request is being retrieved from, but it isn't being taken from the DI at call time.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Di::get() overwrites Di container in instances before return

Hello.

During development, I noticed the strange behavior of phalcon when using Di inside Di.

If you use Di inside Di like this:

$di = new Di();
$di->setShared('app', function() {
  $di = new Di();
  /* some code... */
  $app = new MvcApplication($di);
  /* some code... */
  return $app;
});

$di->get('app')->handle($_GET['_url'] ?? '/');

That in the returned instance Application container will contain the parent Di.
A study of the source showed this:

        /**
         * Pass the DI to the instance if it implements
         * \Phalcon\Di\InjectionAwareInterface
         */
        if typeof instance == "object" {
            if instance instanceof InjectionAwareInterface {
                instance->setDI(this);
            }
        }

Link

The Di::get() method sets container even if it already exists.
Maybe we need to check if the container exists in the instance? See code below:

        /**
         * Pass the DI to the instance if it implements
         * \Phalcon\Di\InjectionAwareInterface
         */
        if typeof instance == "object" {
            if instance instanceof InjectionAwareInterface && !(instance->getDi() instanceof InjectionAwareInterface) {
                instance->setDI(this);
            }
        }

Details

  • Phalcon version: 4.0.0-beta.1
  • PHP Version: PHP 7.0.33-0+deb9u3 (cli) (built: Mar 8 2019 10:01:24) ( NTS )
  • Operating System: debian 9
  • Installation type: Compiling from source
  • Zephir version (if any): Version 0.12.0-c893389

[NFR] UPDATE query with JOIN in PHQL

I have some raw-sql queries, which I want translate to PHQL. For example:

raw sql:

UPDATE versions
LEFT JOIN composerlink ON composerlink.TrackRef=versions.TrackRef
SET Price=PriceOrig
WHERE composerlink.ComposerRef=:artistId
AND (Price=0) OR (Price IS NULL)

And PHQL query of this sql query:

public static function updateVersions($artistId)
{
        $artist = new Artist();
        $phql = "
            UPDATE Version
            LEFT JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef
            SET Version.Price=Version.PriceOrig
            WHERE RelArtistTrack.ComposerRef=:artistId:
            AND (Version.Price=0) OR (Version.Price IS NULL)
        ";
        $result = $artist->getModelsManager()->executeQuery($phql, ['artistId' => $artistId]);
}

So, it's very simple UPDATE query with JOIN. But after executing this query I see an error:

Phalcon\Mvc\Model\Exception: Syntax error, unexpected token LEFT, near to ' JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef\n SET Version.Price=Version.PriceOrig\n WHERE RelArtistTrack.ComposerRef=:artistId:\n AND (Version.Price=0) OR (Version.Price IS NULL)\n ', when parsing: \n UPDATE Version\n LEFT JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef\n SET Version.Price=Version.PriceOrig\n WHERE RelArtistTrack.ComposerRef=:artistId:\n AND (Version.Price=0) OR (Version.Price IS NULL)\n (231)

If I understand correctly, these types of PHQL queries are not supported? Or this is a bug?

phalcon: 2.0.10
Reelated forum thread: https://forum.phalconphp.com/discussion/11065/update-query-with-join-in-phql

Can't get module name from cli dispatcher

phalcon: 3.2.4

description

My project has multiple modules for both mvc application & cli application.

There's many events attached in my project, like 'dispatch:beforeDispatchLoop'.

When this event triggered, from the "getModuleName" method of \Phalcon\Cli\Dispatcher can't get the module name, but \Phalcon\Mvc\Dispatcher did.

The source code of mvc application has called the "setModuleName" method when handle a request,
image

but the cli console hasn't.
image

So in cli mode, how to get the module name from the callback function of dispatch event?

[NFR] ACL on router

I don't know if we might gonna break the framework itself if we will include an additional key like this:

$di->get('router')->addGet('/tickets/{id}/edit', [
    'controller' => '...',
    'action'     => '...',
    'allowed'      => [
        'agent',
        'marketing',
        'moderator',
        'reporter',
        'administrator',
    ],
]);

We have the resource now in which helps a lot for acl, but the code above might be helpful too and might do a maintainable code I think...

we just need to call this function to add a role:

$di->get('acl')->allow('...')

then in the TicketController at editAction() we only need to call something like this

if ( $di->get('acl')->isAllowed() ) {}

Upon dispatching the router, it will analyze if the controller has limited roles, then it will check if the global acl assigned a role that matches on the controller, else it should return a false upon calling isAllowed() function.


FYI: The inputted keys and functions are just my suggestion, but hoping the same approach, it's up to you guys if you want to rename it.

[NFR]: Volt extends from virable or from absolute path

Sometimes there are need to extend layout dynamically. Now it is possible to do only with relative path, ex: {% extends "layouts/base" %}.
Related topic from forum - https://forum.phalcon.io/discussion/19331/volt-extends-from-virable

I would like to have possibility to set variable, relative path with ../ and absolute path inside {% extends ? %}
Examples:

  1. {% extends "../another-theme/layouts/base" %}
  2. {% extends $dynamicLayout %}
  3. {% extends "/var/absolute/path/to/layout" %}

[NFR]: Phalcon Authentication

Can we have a good Authenticaion library/adapther? A good example from Laravel [1] and yes I know about [2] Auth and [3] ACL in phalcon. What I'm talking about is more complete and easier to use library for beginners or those who don't want to code one themselfs. Because everyone coding auth/acl themselfs is unsecure.

[1] https://laravel.com/docs/5.7/authentication

Dangerous collection clone

Phalcon 3.x

If the user tries to create a new behavior to duplicate records, the collection class can create unexpected errors,

Example of a dangerous collection:

class Robot extends Collection
{
	public $name;

	// This method will be triggered at each iteration on the records
	public function __clone()
	{
		$this->_id = new MongoId;
	}

	...
}

The clone method is triggered at each record:

# Part of https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/mvc/collection.zep

protected static function _getResultset(var params, <CollectionInterface> collection, connection, boolean unique)
{
        ...
	if unique === true {

	       /**
		* Requesting a single result
		*/
		documentsCursor->rewind();

		let document = documentsCursor->current();

		if typeof document != "array" {
			return false;
		}

	       /**
		* Assign the values to the base object
		*/
		return static::cloneResult(base, document);
	}

       /**
	* Requesting a complete resultset
	*/
	let collections = [];
	for document in iterator_to_array(documentsCursor, false) {

	       /**
		* Assign the values to the base object
		*/
		let collections[] = static::cloneResult(base, document);
	}

	return collections;
}

The method that clones the result:

# Part of https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/mvc/collection.zep

public static function cloneResult(<CollectionInterface> collection, array! document) -> <CollectionInterface>
{
	var clonedCollection, key, value;

	let clonedCollection = clone collection;
	for key, value in document {
		clonedCollection->writeAttribute(key, value);
	}

	if method_exists(clonedCollection, "afterFetch") {
		clonedCollection->{"afterFetch"}();
	}

	return clonedCollection;
}

Some solutions:

  • Prevent from changing the clone's behavior
abstract class Collection implements EntityInterface, CollectionInterface, InjectionAwareInterface, \Serializable
{
	...

	// Override
	public final function __clone()
	{
	}
}
  • Do not use cloneResult in find/findFirst
  • Put an information message in the documentation

Support Redis Cluster

Hi
As you know as of Redis 3 , this system support clustering and sharding in a way that a system can have redundant session and cache backend .

I checked Zephir codes of Redis backend cache and noticed Phalcon calls it using
new Redis();
new version of phpredis (Redis php driver) now supports clustering and array (not sure when they've added Redis array support but Redis cluster support is somehow new )
but they can't be called using normal new Redis() method and need to be called like these :

$obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"));

This way we will have HA redis cluster for backend cache and session (BTW the driver now supports session clustering and it can be done using php.ini , not tested by myself but i'll test it soon)

So it would be great if we can take advantage of Redis Cluster and Redist array natively in Phalcon .

Router enhancements and changes

Well after some discussion we reverted my latest change to router to 3.2.x which is supposed to take advantage of router prefix to process routes faster. We decided to postpone this change to 4.0.0 with more changes to router. There is a list what i propose:

  • delete Phalcon\Mvc\Router\Group, move necessary methods(like for example setPrefix, setPaths) to Phalcon\Mvc\Router and add Phalcon\Mvc\Router::merge so we can still have separated "route group" classes, just without duplicated/very similar code
  • change Phalcon\Mvc\Router::add so it will accept Phalcon\Mvc\Router\RouteInterface as first argument(keep old behavior too) or add separated method addRoute
  • add factories Route::get, Route::post, etc and Route::via to create route object
  • separate routes into groups with prefixes, add method for getting routes only for certain prefix
  • separate routes into groups with http methods, add methods for getting routes only for certain method
  • instead of checking all routes no matter what prefix/method, get method from request, check correct group, and check prefix if url starts with it - this will improve performance much, when i will finish i will try to do some real case tests with AB
  • additionally add separated class for storing all routes like Phalcon\Mvc\Router\Collection and actual logic for adding routes will happen there, Phalcon\Mvc\Router will only call some methods
  • the problem will be currently code like this:
$router->add("/products", "Products::get")->via(['POST', 'GET']);

The best will be just to throw some exception(just some flag in Route class which will be set after creating and adding it in Phalcon\Mvc\Router) when user will try to do via on this point - because otherwise we will need to do a lot of dirty stuff here(like add it to all method groups and then remove it from not selected method groups etc). So user will need to change his code to:

$router->add("/products", "Products::get", ['POST', 'GET']);

or

$router->add(Route::via('/products', 'Products::get', ['POST', 'GET']));

If someone is using Phalcon\Mvc\Router\Group the only change he will need to do is change extends Router\Group to extends Router and $router->mount to $router->merge.

@michanismus

Model has a small possibility because of replication latency.

I think it's a low priority issue.
but, we should know the possibility of an unintended result.

_exists() function always executes before create() or save() function in Model.

_exists() function => use read connection.
save() function => use write connection.

There may be long latency to replicate between master and slave servers.
We can't be guaranteed that slave servers always are same as master server.
It could be a bit problematic in high transaction.

In my opinion, you'd better use write connection intead of read connection in _exists() function.

[NFR] set properties in Phalcon\Mvc::refresh()

Expected and Actual Behavior

->refresh() uses ->assign()

We use beforeSave, afterFetch and afterSave for property preparation, and we add setters with type hinting.

    /**
     * @param array $json_col
     *
     * @return Books
     */
    public function setJsonCol(array $json_col): Books
    {
        $this->json_col = $json_col;

        return $this;
    }

Unless we set ' disableAssignSetters' => true we get a type error.

 Test  tests/Integration/Behaviours/PropPreparationTraitTest.php:testRefresh
                                                                                                                                 
  [TypeError] Argument 1 passed to CrazyFactory\Phalcon\Test\Models\Books::setJsonCol() must be of the type array, string given  
                                                                                                                                 
phalcon/cphalcon#1  /var/www/project/tests/Models/Books.php:196
phalcon/cphalcon#2  CrazyFactory\Phalcon\Test\Models\Books->setJsonCol
phalcon/cphalcon#3  Phalcon\Mvc\Model->_possibleSetter
phalcon/cphalcon#4  Phalcon\Mvc\Model->assign
phalcon/cphalcon#5  /var/www/project/tests/Integration/Behaviours/PropPreparationTraitTest.php:63
phalcon/cphalcon#6  CrazyFactory\Phalcon\Test\Integration\Behaviours\PropPreparationTraitTest->testRefresh

Reproduce

Model::setup(['disableAssignSetters' => false]);

class Books extends Model
{
    protected $jsonCol;

    /**
     * @param array $json_col
     *
     * @return Books
     */
    public function setJsonCol(array $json_col): Books
    {
        $this->jsonCol = $json_col;

        return $this;
    }

    /**
     * @return array
     */
    public function getJsonCol(): array
    {
        return $this->jsonCol;
    }
   
    public function afterFetch(): void
    {
        $this->jsonCol = json_decode($this->jsonCol, true);
    }

    public function afterSave(): void
    {
        ...

    public function beforeSave(): void
    {
        ...
}

$model = Books::findFirst(1);
// do something
$model->refresh(); // problem time

Solution

Can we set properties in Phalcon\Mvc::refresh() as we do in Phalcon\Mvc::cloneResult() instead of calling assign? Then this->fireEvent("afterFetch"); will fire to prepare properties.

Details

  • Phalcon version: 3.4.3
  • PHP Version: 7.3.6
  • Operating System: Linux
  • Installation type: Compiling from source
  • Server: Nginx
  • Other related info (Database, table schema): MySQL

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.