How to represent a non-empty array? - phpstan

I tried using intersection of types
/**
* #param array&nonEmpty $users
*/
function sayHello($users): void {
echo 'Hello, ' . $users[0];
}
but it complains:
PHPDoc tag #param for parameter $users contains unresolvable type.
(live demo)
If I strip the annotation to just nonEmpty, the error changes to
Parameter $users of function sayHello() has invalid typehint type nonEmpty
I do see the class responsible for the nonEmpty type in the source tree as well as mentioned several times in the tests and issues but I just could not make it work.

Just to provide an answer for those coming from search engines:
PHPStan now has non-empty-array type you can use. Here is an example.

Related

Custom vallidator to ban a specific wordlist

I need a custom validator to ban a specific list of banned words from a textarea field.
I need exactly this type of implementation, I know that it's not logically correct to let the user type part of a query but it's exactly what I need.
I tried with a regExp but it has a strange behaviour.
My RegExp
/(drop|update|truncate|delete|;|alter|insert)+./gi
my Validator
export function forbiddenWordsValidator(sqlRe: RegExp): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
const forbidden = sqlRe.test(control.value);
return forbidden ? { forbiddenSql: { value: control.value } } : null;
};
}
my formControl:
whereCondition: new FormControl("", [
Validators.required,
forbiddenWordsValidator(this.BAN_SQL_KEYWORDS)...
It works only in certain cases and I don't understand why does the same string works one time and doesn't work if i delete a char and rewrite it or sometimes if i type a whitespace the validator returns ok.
There are several issues here:
The global g modifier leads to unexpected alternated results when used in RegExp#test and similar methods that move the regex index after a valid match, it must be removed
. at the end requires any 1 char other than line break char, hence it must be removed.
Use
/drop|update|truncate|delete|;|alter|insert/i
Or, to match the words as whole words use
/\b(?:drop|update|truncate|delete|alter|insert)\b|;/i
This way, insert in insertion and drop in dropout won't get "caught" (=matched).
See the regex demo.
it's not a great idea to give such power to the user

GoAOP framework, can't get simple Aspect to work

I have been trying to play with the GoAOP library for awhile and have never successfully been able to get it to work. I have gone through the documentation several times and copied examples but haven't even been able to get them to work. All I am trying to achieve right now is a simple aspect.
I have several files as below:
app/ApplicationAspectKernel.php
<?php
require './aspect/MonitorAspect.php';
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
/**
* Application Aspect Kernel
*/
class ApplicationAspectKernel extends AspectKernel
{
/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
* #param AspectContainer $container
*
* #return void
*/
protected function configureAop(AspectContainer $container)
{
$container->registerAspect(new Aspect\MonitorAspect());
}
}
init.php
<?php
require './vendor/autoload.php';
require_once './ApplicationAspectKernel.php';
// Initialize an application aspect container
$applicationAspectKernel = ApplicationAspectKernel::getInstance();
$applicationAspectKernel->init(array(
'debug' => true, // Use 'false' for production mode
// Cache directory
'cacheDir' => __DIR__ . '/cache/', // Adjust this path if needed
// Include paths restricts the directories where aspects should be applied, or empty for all source files
'includePaths' => array(__DIR__ . '/app/')
));
require_once './app/Example.php';
$e = new Example();
$e->test1();
$e->test2('parameter');
aspect/MonitorAspect.php
<?php
namespace Aspect;
use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;
/**
* Monitor aspect
*/
class MonitorAspect implements Aspect
{
/**
* Method that will be called before real method
*
* #param MethodInvocation $invocation Invocation
* #Before("execution(public Example->*(*))")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$obj = $invocation->getThis();
echo 'Calling Before Interceptor for method: ',
is_object($obj) ? get_class($obj) : $obj,
$invocation->getMethod()->isStatic() ? '::' : '->',
$invocation->getMethod()->getName(),
'()',
' with arguments: ',
json_encode($invocation->getArguments()),
"<br>\n";
}
}
app/Example.php
<?php
class Example {
public function test1() {
print 'test1' . PHP_EOL;
}
public function test2($param) {
print $param . PHP_EOL;
}
}
When I run php init.php it does run but just prints without the output from MonitorAspect. I don't know if I'm defining the pointcut wrong in the #Before (I've tried several variations) or if I just have a fundamental misunderstanding of how this code is suppose to work.
Any help to point me in the right direction would be greatly appreciated.
GoAOP framework was designed to work with autoloader this means that it can handle only classes that were loaded indirectly via composer autoloader.
When you manually include you class via require_once './app/Example.php'; class is loaded by PHP immediately and could not be transformed by AOP, so nothing happens, because class is already present in the PHP's memory.
In order to make AOP working you should delegate class loading to the Composer and use PSR-0/PSR-4 standard for your classes. In this case, AOP will hook autoloading process and will perform transformation when needed.
See my answer about how AOP works in plain PHP that doesn't require any PECL-extentions for additional details about internals of the framework. This information should be useful for you.

UploadableFilePath field in entity definition is ignored (uploadable doctrine extensions)

Summary
My problem is connected with the fact that the entity field marked with #Gedmo\UploadableFilePath annotation is ignored by Symfony3. I am using the Uploadable behavior extension for Doctrine2.
CODE
In my entity I have:
/**
* #ORM\Column
* #Gedmo\UploadableFileName
*/
private $name;
/*
* #ORM\Column
* #Gedmo\UploadableFilePath
*/
private $path;
SYMPTOMS
At first, I have noticed though that the path column is not generated in MySQL.
Then I found out that whenever I delete name field I get the following error:
[Gedmo\Exception\InvalidMappingException] Class
"AppBundle\Entity\plik" must have an UploadableFilePath or Uploadable
FileName field.
Documentation
In doctrine-uploadable documentation I see that:
#Gedmo\Mapping\Annotation\UploadableFilePath: This annotation is used
to set which field will receive the path to the file. The field MUST
be of type "string". Either this one or UploadableFileName annotation
is REQUIRED to be set.
so it seems that I should be able to set $path field only.
Help request
Please advice why is UploadableFilePath field not being generated and why can't I delete the $name field?
It seems that I have made a simple typo. There was lack of one * in the comment line.
/**
* #ORM\Column
* #Gedmo\UploadableFilePath
*/
private $path;
It seems that symfony ignores annotations in such a case.

Flow: Convert object to array for CSV export

I want to export my object "mitglied" to a .csv-file. My controller looks like that:
public function exportAction() {
// find all mitglieds
$records = $this->mitgliedRepository->findTennis();
// Set path for export-file
$csvPath = '/var/www/apps/flow/Packages/Application/ITOOP.Atc/Resources/Private/Export/test.csv';
$fp = fopen($csvPath, 'w');
foreach ($records as $lines) {
fputcsv($fp, $lines);
}
fclose($fp);
}
When I call the exportAction, I get a an error:
#1: Warning: fputcsv() expects parameter 2 to be array, object given in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Controller_MitgliedController.php line 494
line 494 is...
fputcsv($fp, $lines);
...so I think I have to convert the object "mitglied" to an array.
My the public function findTennis in my mitgliedRepository looks like that:
public function findTennis() {
$query = $this->createQuery();
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute();
return $result;
}
I tried to set toArray(); in the repository like the that:
public function findTennis() {
$query = $this->createQuery();
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute()
->toArray;
return $result;
}
But then I get the following error:
#1: Notice: Undefined property: TYPO3\Flow\Persistence\Doctrine\QueryResult::$toArray in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Domain_Repository_MitgliedRepository.php line 105
line 105 of course is
->toArray;
Does anybody know, how to convert an object to an array in flow?
With the following example the export works, so I think the (formatting of the) repository query is the problem.
public function exportAction() {
// Set path for export-file
$csvPath = '/var/www/apps/flow/Packages/Application/ITOOP.Atc/Resources/Private/Export/test.csv';
$test = array (
array('xxx', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('aaa', 'bbb')
);
$fp = fopen($csvPath, 'w');
foreach ($test as $lines) {
fputcsv($fp, $lines);
}
fclose($fp);
}
Please point me to the right direction. Thank you!
The error messages explained
#1: Warning: fputcsv() expects parameter 2 to be array, object given in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Controller_MitgliedController.php line 494
fputcsv expects it's 2nd parameter to be an array. That array will be written as a CSV line into single file, with each array element as column. When iterating over your $records variable, you get instances of your domain object class (so probably sth. like ITOOP\Atc\Domain\Model\Mitglied). That's undefined behaviour, thus the warning.
#1: Notice: Undefined property: TYPO3\Flow\Persistence\Doctrine\QueryResult::$toArray in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Domain_Repository_MitgliedRepository.php line 105
toArray is a function that is offered by Doctrine QueryResult class. Typically, Doctrine queries do not fetch all objects returned by the query, but return an iterator that fetches and maps entities on-demand. The toArray method fetches all records at once and returns an array instead of the iterator. Your error occurs, because you try to access toArray as a property, and not calling it as a method. The following code would be correct:
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute()
->toArray(); // <- Mind the brackets!
However, this will not help you anything, because in your controller, you will still be iterating over a list of domain entities (foreach does not care if its iterating over an iterator or an array; that's actually the point of iterators in PHP).
Quick&Dirty solution
Convert your domain entities by hand in your controller. Only you can know how your CSV export should look like, so this cannot be automated. I'm thinking something like this:
foreach ($records as $record) {
$csvLine = [
$record->getFirstProperty(),
$record->getSecondProperty(),
// and so on...
];
fputcsv($fp, $csvLine);
}
Better solution
Rendering CSV data is not a concern that should be addressed in the controller. Basically, it should go into a view. You can implement a custom view class for handling the CSV output.
For that, you need to implement the \TYPO3\Flow\Mvc\View\ViewInterface. The easiest way to do this is to subclass \TYPO3\Flow\Mvc\View\AbstractView. Name your view class <PackageNamespace>\View\<Controller>\Action<Format> (so sth. like ITOOP\Atc\View\Mitglied\ExportCsv. Implement your CSV export logic in the view's render() method. Flow will pick up and use the view class automatically as soon as it's present.
Implementing custom views is explained in depth in this article -- it's in German though, although based on your class naming I suspect that won't be a problem ;).
I solved the problem with arbitrary DQL. As I mentioned I think the problem was that I didn't got an array as result by the query. But with the following query in my repository I do:
/**
* #Flow\Inject
* #var \Doctrine\Common\Persistence\ObjectManager
* inject Doctrine's EntityManager to execute arbitrary DQL
*/
protected $entityManager;
/**
* find mitglieder with Abteilung Tennis und return an array
*/
public function exportTennis() {
$query = $this->entityManager->createQuery("SELECT mitglied FROM \itoop\atc\Domain\Model\Mitglied mitglied WHERE mitglied.abteilung = 'Tennis'");
return $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}
The important part I think is getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);

Joomla 2.5 content plugin

I'm trying to deploy a simple plugin to a Joomla 2.5 installation. The code in the plugin that is outside the class declaration runs and adds the two script tags to the head. However, the code within does nothing. I can't change the $article->title or $article->text. I've copy and pasted, verbatim from different articles, but everything seems to talk only about 1.5. The 1.7 stuff that I do find only mentions changing onPrepareContent to onContentPrepare. Neither seems to do anything. I would appreciate any help!
<?php
// No direct access.
defined( '_JEXEC' ) or die( 'Restricted access' );
class plgContentPicasaGallery extends JPlugin
{
/**
*
* #param string The context of the content being passed to the plugin.
* #param mixed An object with a "text" property.
* #param array Additional parameters.
* #param int Optional page number. Unused. Defaults to zero.
* #return boolean True on success.
*/
public function onContentBeforeDisplay($context, &$article, &$params, $page = 0)
{
if (is_object($article)) {
$article->text = "omfg, wtf?";
return true;
} else {
$article = "omfg, I'm not an object, wtf?";
return true;
}
}
}
Joomla documentation & tutorials a little bit out dated, new framework changed few things.
To find proper signatures simply look at /plugins/content/... files.
Below is proper function signature & phpdoc for onContentPrepare.
/**
* #param string The context of the content being passed to the plugin.
* #param object The article object. Note $article->text is also available
* #param object The article params
* #param int The 'page' number
*/
public function onContentPrepare($context, &$article, &$params, $page = 0)
{
...
}
My noobishness with Joomla prevailed over my good sense. I was editing the plugin files on the server and I was expecting that to update the plugin. Thanks for the help!
you can use this method
jimport('joomla.form.helper');
$urla= JRequest::getVar('id');
$urlview= JRequest::getVar('view');
if ($urlview=='article')
{}
if ($urla==10<- number id article )
{}
i know framework joomla is good but its for understanding method