codeigniter Cannot access protected property MY_Loader::$_ci_cached_vars - php

After upgrade of Codeigniter i get this message
Cannot access protected property MY_Loader::$_ci_cached_vars
i know that this property is now protected so i change
else if (isset($CI->load->_ci_cached_vars[$key]))
{
$val = $CI->load->_ci_cached_vars[$key];
}
to
if (isset($CI->load->get_var($key)))
{
$val = $CI->load->get_var($key);
}
but then i get
Can't use method return value in write context
this is get_var method
/**
* Get Variable
*
* Check if a variable is set and retrieve it.
*
* #param array
* #return void
*/
public function get_var($key)
{
return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
}
what can i do, just use
if ($CI->load->get_var($key)) != null) {
$val = $CI->load->get_var($key);
}
without isset? i want to check if is not NULL, becouse get_var method return null
or is if ($CI->load->get_var($key))) { check enough?

You cannot use isset on a function
i.e. $CI->load->get_var($key) will always return "something" - but what that "something" is depends.
So you are correct - the code below will achieve your goal. If the function returns "null" - then isset already failed. If the function returns something else (besides null) - then you will have a valid return.
if ($CI->load->get_var($key)) != null) {
$val = $CI->load->get_var($key);
}

Related

InvalidArgumentException The current node list is empty

i am trying web scraping so i get this error on "\vendor\symfony\dom-crawler\Crawler.php:552"
here is the crawler.php code what the browser showed me:
#throws \InvalidArgumentException When current node is empty
*/
public function text(string $default = null, bool $normalizeWhitespace = true): string
{
if (!$this->nodes) {
if (null !== $default) {
return $default;
}
throw new \InvalidArgumentException('The current node list is empty.');
}
$text = $this->getNode(0)->nodeValue;
if ($normalizeWhitespace) {
return trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text));
}
return $text;
}
This exception is thrown when trying to access the text of an empty node list. I have written a util class for this purpose, as I don't always mind the value being empty. Simply pass the node list you want to get the inner text from, like so CrawlUtil::innerText($nodeList). Hope it will help somebody in the future :)
use Symfony\Component\DomCrawler\Crawler;
class CrawlUtil
{
/**
* Suppresses exception that node list is empty.
*
* #param Crawler $crawler
*
* #return string|null
*/
public static function innerText(Crawler $crawler): ?string
{
if ($crawler->count() > 0 && $crawler->text() !== '') {
return $crawler->innerText();
}
return null;
}
}

Using Query Result Count for Validation Rule (Even when empty)

I've made a custom validation rule fiveMaxThemesChoice which consists as the name may explain it in limiting a user (Etudiant) to a maximum choice of 5 themes in the app I'm building. This is the logic below with EtudiantsChoixTheme the Eloquent where I register the choices:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use App\EtudiantsChoixTheme;
class fiveMaxThemesChoice implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
$choixEtudiants = EtudiantsChoixTheme::all();
foreach ($choixEtudiants as $choixEtudiant) {
$IdEtudiantOccurences = count($choixEtudiants->where('idEtudiant', auth()->user()->id));
if($IdEtudiantOccurences <= 5){
return true;
}
else{
return false;
}
}
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'You have already chosen a maximum of 5 themes.';
}
}
When there is at least 1 record in the table, this works. The issue is that when the table get empty, the validation error message is returned even if there isn't any record yet in the table therefore not the maximum 5 (so <= 5 is supposed to be right). Why is that not working tho?
I've tried with different syntax :
$IdEtudiantOccurences = count($choixEtudiants->where('idEtudiant', auth()->user()->id)->first());
$IdEtudiantOccurences = $choixEtudiants->where('idEtudiant', auth()->user()->id)->first()->count();
$IdEtudiantOccurences = $choixEtudiants->where('idEtudiant', auth()->user()->id)->count();
The same problem persists when the table is empty. Does anyone have an idea of what can be the problem here? Any help or suggestion is welcome
<?php
declare(strict_types=1);
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use App\EtudiantsChoixTheme;
final class FiveMaxThemesChoice implements Rule
{
private const MAXIMUM_NUMBER_OF_THEMES = 5;
public function passes($attribute, $value): bool
{
$idEtudiant = auth()->user()->id;
$etudiantsQuery = EtudiantsChoixTheme::where('idEtudiant', $idEtudiant);
return $etudiantsQuery->count() <= self::MAXIMUM_NUMBER_OF_THEMES;
}
public function message(): string
{
return 'You have already chosen a maximum of 5 themes.';
}
}
Instead of fetching all (EtudiantsChoixTheme::all();) try doing the query directly and asking for the total after it (EtudiantsChoixTheme::where(...)->count())
I took this idea from the official docu: https://laravel.com/docs/4.2/queries
So here is your original body for passes():
public function passes($attribute, $value)
{
$choixEtudiants = EtudiantsChoixTheme::all();
foreach ($choixEtudiants as $choixEtudiant) {
$IdEtudiantOccurences = count($choixEtudiants->where('idEtudiant', auth()->user()->id));
if($IdEtudiantOccurences <= 5){
return true;
}
else{
return false;
}
}
// So what value is returned here? PHP says NULL
}
For the foreach() loop to execute, it required that the collection be larger or equal to 1. If it is empty, it won't be executed (hence neither of your explicit return statements being executed.)
public function passes($attribute, $value)
{
$choixEtudiants = EtudiantsChoixTheme::all();
foreach ($choixEtudiants as $choixEtudiant) {
$IdEtudiantOccurences = count($choixEtudiants->where('idEtudiant', auth()->user()->id));
if($IdEtudiantOccurences <= 5){
return true;
}
else{
return false;
}
}
return false ;
}
This version addresses that one lacking requirement.

PHP override weak function return type coercion

According to the PHP documentation on Strict Typing found here
By default, PHP will coerce values of the wrong type into the expected scalar type if possible. For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.
I'm curious if there is a way to override this functionality to customize the way that the coercion is done.
For example
function getResponse() : \Namespace\Response {
return []; // Ideally, this would be coerced into a Response object.
}
. . .
namespace Namespace;
class Response {
public $data;
public function __construct(array $arr)
{
$this->data = $arr;
}
public static function __coerce($value)
{
if (! is_array($value)) {
throw new \TypeError('Wrong type during coercion.');
}
return new self($value);
}
}
This is not possible as this is evalue on language level on compile time.
Only thing you can do is override parent return type:
public function getResponse(): [] // though parent has "Reponse" type
{
return [];
}
I've written my own implementation to do this in PHP, since none existed. This is how it works.
These are the two base functions.
The multiReturnFunction function. (Used for calling global functions and anon functions)
/**
* Call a global function and use type coercion
* for non-registered return types.
*
* #param closure $closure The function to execute.
* #param string $returnType The registered return type.
* #param array $params The parameters to pass to the function.
*
* #return mixed The result of the function to execute.
*/
function multiReturnFunction($closure, $returnType, ...$params)
{
$val = $closure(...$params);
if (gettype($val) === 'object') {
if (get_class($val) != $returnType) {
if (method_exists($returnType, '__coerce')) {
$val = $returnType::__coerce($val);
} else {
throw new \Exception(
'Returned value does not match the return type defined, '.
'and no __coerce function is visible.'
);
}
}
} else if (gettype($val) != $returnType) {
if (method_exists($returnType, '__coerce')) {
$val = $returnType::__coerce($val);
} else {
throw new \Exception(
'Returned value does not match the return type defined, '.
'and no __coerce function is visible.'
);
}
}
return $val;
}
The multiReturnFunction function will call a closure and use the __coerce function of the return type class to coerce the return type if the resulting return type does not match.
An Example of the multiReturnFunction function
Define the class that we will be using, and be sure to give it a __coerce function.
Note: __coerce functions take a single variable for the object that we will be trying to coerce into this class type. The function must be declared static.
class MyClass
{
private $data;
public function __construct(array $value)
{
$this->data = $value;
}
public static function __coerce($value)
{
if (! is_array($value)) {
throw new \Exception(
'Returned value does not match the return type defined.'
);
}
return new self($value);
}
}
Next, you will need to call the multiReturnFunction function using your class and an anonymous function.
$resultingMyClass = multiReturnFunction (
// Multi return type function.
function($name, $age) {
// Here you can return either a MyClass instance, or an array.
// All other types will throw an exception.
return [$name, $age];
},
// Return Type, any other type will be coerced through this class.
MyClass::class,
// Function parameters.
'Nathan', 23
);
The multiReturnMethod function. (Used for calling class methods)
/*
* Call a class method and use type coercion
* for non-registered return types.
*
* #param object $obj The object to call the method on.
* #param string $method The method to call on the object.
* #param string $returnType The registered return type.
* #param array $params The parameters to pass to the method.
*
* #return mixed The result of the method to execute.
*/
function multiReturnMethod($obj, $method, $returnType, ...$params)
{
$val = $obj->{$method}(...$params);
if (gettype($val) === 'object') {
if (get_class($val) != $returnType) {
if (method_exists($returnType, '__coerce')) {
$val = $returnType::__coerce($val);
} else {
throw new \Exception(
'Returned value does not match the return type defined, '.
'and no __coerce function is visible.'
);
}
}
} else if (gettype($val) != $returnType) {
if (method_exists($returnType, '__coerce')) {
$val = $returnType::__coerce($val);
} else {
throw new \Exception(
'Returned value does not match the return type defined, '.
'and no __coerce function is visible.'
);
}
}
return $val;
}
The multiReturnMethod function will call a class method and use the __cooerce function of the return type class to coerce the return type if the resulting return type does not match.
An Example of the multiReturnMethod function
Note: We will use the MyClass class we created before as a return type.
Define a class that we can use to call the class method on.
class MRFClass {
function callMe($name)
{
return [$name, 1, 2, 3];
}
}
Call the multiReturnMethod function using your class and an anonymous function.
$resultingMyClass = multiReturnMethod (
// The object that we will be calling the
// class method on.
new MRFClass(),
// The class method to call, by name.
'callMe',
// The return type to coerce to.
MyClass::class,
// The function parameters.
'Nathan'
);
Both of these methods can be used to force strict return type functions, that accept multiple return types that can be coerced to the return type on a per-class implementation.

Nullable boolean with PostGres in Symfony 2.0

I have a class with the following attribute :
/**
* #var boolean
*
* #ORM\Column(name="validated", type="boolean", nullable=true)
*/
private $validated;
I would like it to be null by default in the database but whatever I do I keep getting a FALSE value when inserting it as NULL.
Does anybody have an idea on how to achieve this?
Default values come from your entities, so this should do it
private $validated = null;
Never used postgre but it appears that there is a function which return false for null value (convertSingleBooleanValue) as shown here :
private function convertSingleBooleanValue($value, $callback)
{
if (null === $value) {
return $callback(false);
}
if (is_bool($value) || is_numeric($value)) {
return $callback($value ? true : false);
}
if (!is_string($value)) {
return $callback(true);
}
//etc...
}
https://github.com/doctrine/dbal/pull/625
So as this is most likely your problem, you should check if there is a way to bypass this.
As for postgresql http://www.postgresql.org/docs/8.1/static/datatype-boolean.html there is still an "unknown" value that is null.

One field should not be blank if some fields are blank in Symfony Form

In my Symfony 2 (2.4.2) application, there is a Form Type which consists of 3 fields.
I'd like the validation be like this: If field A and field B are blank, field C should not be blank. This means that at least one field should receive some data.
Currently, I check the received data in the controller. Is there a more recommended way to do this?
There are even easier solutions than writing a custom validator. The easiest of all is probably the expression constraint:
class MyEntity
{
private $fieldA;
private $fieldB;
/**
* #Assert\Expression(
* expression="this.fieldA != '' || this.fieldB != '' || value != ''",
* message="Either field A or field B or field C must be set"
* )
*/
private $fieldC;
}
You can also add a validation method to your class and annotate it with the Callback constraint:
/**
* #Assert\Callback
*/
public function validateFields(ExecutionContextInterface $context)
{
if ('' === $this->fieldA && '' === $this->fieldB && '' === $this->fieldC) {
$context->addViolation('At least one of the fields must be filled');
}
}
The method will be executed during the validation of the class.
This is probably a use case for a Custom Validation Constraint. I haven't used it myself but basically you create a Constraint and a Validator. You then specify your Constraint in your config/validation.yml.
Your\Bundle\Entity\YourEntity:
constraints:
- Your\BundleValidator\Constraints\YourConstraint: ~
The actual validation is done by your Validator. You can tell Symfony to pass the whole entity to your validate method to access multiple fields with:
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
And your validate:
public function validate($entity, Constraint $constraint)
{
// Do whatever validation you need
// You can specify an error message inside your Constraint
if (/* $entity->getFieldA(), ->getFieldB(), ->getFieldC() ... */) {
$this->context->addViolationAt(
'foo',
$constraint->message,
array(),
null
);
}
}
You can do this with Group Sequence Providers, for example:
use Symfony\Component\Validator\GroupSequenceProviderInterface;
/**
* #Assert\GroupSequenceProvider
*/
class MyObject implements GroupSequenceProviderInterface
{
/**
* #Assert\NotBlank(groups={"OptionA"})
*/
private $fieldA;
/**
* #Assert\NotBlank(groups={"OptionA"})
*/
private $fieldB;
/**
* #Assert\NotBlank(groups={"OptionB"})
*/
private $fieldC;
public function getGroupSequence()
{
$groups = array('MyObject');
if ($this->fieldA == null && $this->fieldB == null) {
$groups[] = 'OptionB';
} else {
$groups[] = 'OptionA';
}
return $groups;
}
}
Not tested it but I think it would work
I know that it's old question but you can also use the NotBlankIf validator from this bundle https://github.com/secit-pl/validation-bundle
<?php
use SecIT\ValidationBundle\Validator\Constraints as SecITAssert;
class Entity
{
private ?string $fieldA = null;
private ?string $fieldB = null;
#[SecITAssert\NotBlankIf("!this.getFieldA() and !this.getFieldB()")]
private ?string $fieldC = null;
public function getFieldA(): string
{
return $this->fieldA;
}
public function getFieldB(): string
{
return $this->fieldB;
}
}

Categories