Laminas Zend Framework 2 & 3 – Bitkorn Blog https://blog.bitkorn.de Developer Zeugz Sun, 07 Nov 2021 08:33:23 +0000 de-DE hourly 1 https://wordpress.org/?v=6.3.1 filter_input_array() UUIDs https://blog.bitkorn.de/filter_input_array-uuids/ Sun, 07 Nov 2021 07:13:38 +0000 https://blog.bitkorn.de/?p=1060 Code sagt mehr als tausend Worte:

<?php
/**
 * Use a callback function to filter UUIDs from POST with filter_input_array()
 * c0fc2876-2551-426b-8cc1-69730d22774a
 * 2c91ff35-7089-4383-91e3-0976a7bf06a9
 * ddc25d51-d2a7-4b32-9ca8-3cf70d89dffb
 */
require '../../vendor/autoload.php';

use Laminas\Validator\Uuid;

$uuid = new Uuid();
function filterUuid(string $value): string
{
    global $uuid;
    return $uuid->isValid($value) ? $value : '';
}

class MyStaticFilter
{
    protected static Uuid $uuid;

    public static function filterUuid(string $value): string
    {
        if (!isset(self::$uuid)) {
            self::$uuid = new Uuid();
        }
        return self::$uuid->isValid($value) ? $value : '';
    }
}

class MyFilter
{
    protected Uuid $uuid;

    public function filterUuid(string $value): string
    {
        if (!isset($this->uuid)) {
            $this->uuid = new Uuid();
        }
        return $this->uuid->isValid($value) ? $value : '';
    }
}

$myFilter = new MyFilter();

$call = (int)filter_input(INPUT_POST, 'callback', FILTER_SANITIZE_NUMBER_INT);
switch ($call) {
    case 1:
        $uuids = filter_input_array(INPUT_POST, ['anuuid' => [
            'filter'  => FILTER_CALLBACK,
            'flags'   => FILTER_REQUIRE_ARRAY,
            'options' => 'filterUuid',
        ]]);
        break;
    case 2:
        $uuids = filter_input_array(INPUT_POST, ['anuuid' => [
            'filter'  => FILTER_CALLBACK,
            'flags'   => FILTER_REQUIRE_ARRAY,
            'options' => ['MyStaticFilter', 'filterUuid'],
        ]]);
        break;
    case 3:
        $uuids = filter_input_array(INPUT_POST, ['anuuid' => [
            'filter'  => FILTER_CALLBACK,
            'flags'   => FILTER_REQUIRE_ARRAY,
            'options' => [$myFilter, 'filterUuid'],
        ]]);
        break;
    default:

}

$uuids = $uuids['anuuid'] ?? [];
?>
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8">
    <title>filter uuid</title>
    <link href="../../../w3.css" media="screen" rel="stylesheet" type="text/css">
<body>
<div class="w3-container">
    <form action="filter-uuid.php" method="post" style="width: 400px">
        <label class="w3-block">1. UUID <input type="text" class="w3-input" name="anuuid[]" value="<?php echo $uuids[0] ?? '' ?>"></label>
        <label class="w3-block">2. UUID <input type="text" class="w3-input" name="anuuid[]" value="<?php echo $uuids[1] ?? '' ?>"></label>
        <label class="w3-block">4. UUID <input type="text" class="w3-input" name="anuuid[]" value="<?php echo $uuids[2] ?? '' ?>"></label>
        <select class="w3-select" name="callback">
            <option value="1" <?php echo $call == 1 ? 'selected="selected"' : '' ?>>function</option>
            <option value="2" <?php echo $call == 2 ? 'selected="selected"' : '' ?>>static</option>
            <option value="3" <?php echo $call == 3 ? 'selected="selected"' : '' ?>>object</option>
        </select>
        <button type="submit" class="w3-button w3-blue-gray">ok</button>
    </form>
</div>
<div class="w3-container">
    <pre><?= print_r($uuids, true) ?></pre>
</div>
</body>

https://www.php.net/manual/de/function.filter-input-array

https://www.php.net/manual/en/filter.filters.misc.php

https://www.php.net/manual/de/language.types.callable.php

]]>
ViewHelper im Service benutzen https://blog.bitkorn.de/viewhelper-im-service-benutzen/ Fri, 23 Jul 2021 09:13:59 +0000 https://blog.bitkorn.de/?p=1004 Zuerst etwas config:

'view_helpers'        => [
    'aliases'    => [
    ],
    'factories'  => [
        FooterHtml::class => FooterHtmlFactory::class,
    ],

In einer Factory den ViewHelperManager/HelperPluginManager holen und ausführen:

/** @var HelperPluginManager $pluginManager */
$pluginManager = $container->get('ViewHelperManager');
/** @var FooterHtml $footerHtml */
$footerHtml = $pluginManager->get(FooterHtml::class);
$some->setUserdataFooterHtml($footerHtml());

Und der ViewHelper:

class FooterHtml extends AbstractViewHelper
{
    const TEMPLATE = 'lerpDocumentTcpdf/footerHtml';

    protected string $brandColor;

    public function setBrandColor(string $brandColor): void
    {
        $this->brandColor = $brandColor;
    }

    /**
     * @return string
     */
    public function __invoke(): string
    {
        $viewModel = new ViewModel();
        $viewModel->setTemplate(self::TEMPLATE);
        $viewModel->setVariable('colorBrand', $this->brandColor);
        return $this->getView()->render($viewModel);
    }
}
]]>
ZF3 Pdo_Pgsql LastGeneratedValue https://blog.bitkorn.de/zf3-pdo_pgsql-lastgeneratedvalue/ Sat, 28 Dec 2019 15:31:02 +0000 http://blog.bitkorn.de/?p=702 Nur mit Angabe der Sequence funktioniert es:

$lastId = $this->getAdapter()->getDriver()->getConnection()->getLastGeneratedValue('public.seq_some_name');

stackoverflow

]]>
EventManager and Listener class https://blog.bitkorn.de/eventmanager-and-listener-class/ Sat, 13 Jul 2019 11:36:12 +0000 http://blog.bitkorn.de/?p=629 Ein Listener:

namespace Lcamo\CamoTransfer\Event;

use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Log\Logger;

class CamoListener extends AbstractListenerAggregate
{
    protected $listeners = [];

    /**
     * @var Logger
     */
    protected $logger;

    /**
     * @param Logger $logger
     */
    public function setLogger(Logger $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * Attach one or more listeners
     *
     * Implementors may add an optional $priority argument; the EventManager
     * implementation will pass this to the aggregate.
     *
     * @param EventManagerInterface $events
     * @param int $priority
     * @return void
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        $this->listeners[] = $events->attach('before_login', array($this, 'beforeLogin'));
    }

    public function beforeLogin(EventInterface $event)
    {
        $params = $event->getParams();
        $this->logger->info(print_r($params, true));
    }
}

Factory des Listeners (hier bekommt der Listener den EventManager vom UserService injiziert:

namespace Lcamo\CamoTransfer\Factory;

use Bitkorn\User\Service\UserService;
use Interop\Container\ContainerInterface;
use Lcamo\CamoTransfer\Event\CamoListener;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;

class CamoListenerFactory implements FactoryInterface
{

    /**
     * Create an object
     *
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param null|array $options
     * @return object
     * @throws ServiceNotFoundException if unable to resolve the service.
     * @throws ServiceNotCreatedException if an exception is raised when
     *     creating a service.
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        /** @var UserService $userService */
        $userService = $container->get(UserService::class);
        $eventManager = $userService->getEvents();
        $listener = new CamoListener();
        $listener->setLogger($container->get('logger'));
        $listener->attach($eventManager);
        return $listener;
    }
}

Den Listener in der /config/application.config.php eintragen:

use Lcamo\CamoTransfer\Event\CamoListener;

return [
    // Retrieve list of modules used in this application.
    'modules' => require __DIR__ . '/modules.config.php',
    'listeners' => [
        CamoListener::class
    ],
    // These are various options for the listeners attached to the ModuleManager
    'module_listener_options' => [
        // This should be an array of paths in which modules reside.
        // If a string key is provided, the listener will consider that a module
        // namespace, the value of that key the specific path to that module's
        // Module class.
        'module_paths' => [
            './module',
            './vendor',
        ],

        // An array of paths from which to glob configuration files after
        // modules are loaded. These effectively override configuration
        // provided by modules themselves. Paths may use GLOB_BRACE notation.
        'config_glob_paths' => [
            realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php',
        ],
        // ...
];

Und der UserService, der den EventManager hält und das Event triggert:

namespace Bitkorn\User\Service;

use Zend\EventManager\EventManager;

class UserService
{

    /**
     * @var EventManager
     */
    protected $events;

    /**
     * @return EventManager
     */
    public function getEvents(): EventManager
    {
        return $this->events;
    }

    /**
     *
     * @param string $login
     * @param string $password
     * @return bool
     */
    public function loginWithLoginAndPassword(string $login, string $password): bool
    {
        $this->events->trigger('before_login', $this, ['login' => $login, 'passwd' => $password]);

        /**
         * ...some other stuff
         */
        return true;
    }

    /**
     * ...some other stuff
     */
}

U.a. Dank an Samsonasik.

]]>
createDriver expects a „driver“ key to be present inside the parameters https://blog.bitkorn.de/createdriver-expects-a-driver-key-to-be-present-inside-the-parameters/ Sat, 25 May 2019 09:34:13 +0000 http://blog.bitkorn.de/?p=602 Ist man im Development Mode braucht es einen default Datenbank Adapter in /config/autoload/db.local-development.php.

    'db' => array(
        // for primary db adapter that called
        // by $sm->get('Zend\Db\Adapter\Adapter')
        // required in development mode
        'username' => $dbParams['username'],
        'password' => $dbParams['password'],
        'driver' => 'Pdo_Pgsql',
        'dsn' => 'pgsql:dbname=' . $dbParams['database'] . ';host=' . $dbParams['hostname'],
        'adapters' => array(
            'dbDefault' => array(
                'username' => $dbParams['username'],
                'password' => $dbParams['password'],
                'driver' => 'Pdo_Pgsql',
                'dsn' => 'pgsql:dbname=' . $dbParams['database'] . ';host=' . $dbParams['hostname']
            ),
        ),
    ),

zend-developer-tools/issues/224#issuecomment-246174677

Bei der Zend Skeleton Application ist man zu Begin im Development Mode
Development Mode ausschalten:

vendor/bin/zf-development-mode disable

oder einschalten:

vendor/bin/zf-development-mode enable

Im Development Mode hat man die Developer Toolbar.

]]>
ZF2 merged configuration prevent duplicate https://blog.bitkorn.de/zf2-merged-configuration-prevent-duplicate/ Fri, 09 Feb 2018 10:22:38 +0000 http://blog.t-brieskorn.de/?p=367 Bei der Skeleton Application ist die System Konfiguration in /config/application.config.php. Und dort unter dem Key module_listener_options steht so etwas:

        // An array of paths from which to glob configuration files after
        // modules are loaded. These effectively override configuration
        // provided by modules themselves. Paths may use GLOB_BRACE notation.
        'config_glob_paths' => array(
            'config/autoload/{{,*.}global,{,*.}local}.php',
        ),

Also, ZF2 überschreibt/merged die Modulkonfiguration mit denen in config/autoload/. Priorität hat global über Modul und local über global.

Ist ein Array Value(s) in der config ein Null indiziertes Array, werden die Values, falls doppelt, NICHT in der obigen Priorität ersetzt. Also kein array_unique(). Man bekommt doppelte, oder mehr, Values.

Habe ich z.B. folgendes in meiner MyModule/config/module.config.php

    'my_module' => [
        'some_module_conf' => [
            'first',
            'second'
        ]
    ]

Und um beim Deployen nicht diese ureigene Config zu verändern weil sie in einer Versionsverwaltung ist, habe ich unter [webroot]/config/autoload/ eine Datei merged-modules.local.php und eine merged-modules.global.php mit den verschiedenen Configs der verschiedenen Module meiner Application.

return [
    'my_module' => [
        'some_module_conf' => [
            'first',
            'second'
        ]
    ],
    'other_module' => [
        'other_module_stuff' => [
            'somekey' => 4,
        ]
    ]
];

Mache ich es wie oben gezeigt erhalte ich doppelte Values und zwar aus jeder Config die ZF2 merged. In diesem Fall sähe das so nach dem Mergen aus:

    'my_module' => [
        'some_module_conf' => [
            'first',
            'second',
            'first',
            'second',
            'first',
            'second'
        ]
    ],

Um das zu verhindern und jede Value nur einmal zu haben ändere ich die Null Indizierung:
In der MyModule/config/module.config.php:

    'my_module' => [
        'some_module_conf' => [
            'first' => 'first',
            'second' => 'second',
        ]
    ],

Und auch in den [webroot]/config/autoload/{{,*.}global,{,*.}local}.php:

return [
    'my_module' => [
        'some_module_conf' => [
            'first' => 'first',
            'second' => 'second',
        ]
    ],
    'other_module' => [
        'other_module_stuff' => [
            'somekey' => 4,
        ]
    ]
];
]]>
ZF2: Attempting to quote a value without specific driver level support can introduce security vulnerabilities in a production environment https://blog.bitkorn.de/zend-attempting-to-quote-a-value-without-specific-driver-level-support-can-introduce-security-vulnerabilities-in-a-production-environment/ Mon, 04 Dec 2017 09:34:07 +0000 http://blog.t-brieskorn.de/?p=353 Try this for debugging SQL queries

$this->logger->debug($select->getSqlString());

throws a Notice: Attempting to quote a value without specific driver level support can introduce security vulnerabilities in a production environment.

To get also (not only remove the notice) a correct SQL query:

$this->logger->debug($select->getSqlString($this->adapter->getPlatform()));

For Platform MySQL it makes not correct query. An integer from SQL query LIMIT part

$select->limit(1);

give me

LIMIT '1'

what throws an error in MySQL.
Correct is

LIMIT 1
]]>
ZF2 subquery https://blog.bitkorn.de/zf2-subquery/ Thu, 02 Nov 2017 10:11:32 +0000 http://blog.t-brieskorn.de/?p=315 public function subquery() { $sql = new \Zend\Db\Sql\Sql($this->adapter); $select = $sql->select()->from('comment'); $selectSub = $sql->select() ->from('comment_vote') ->columns(['negativeVote' => new \Zend\Db\Sql\Expression('COUNT(comment_vote.id)')]) ->where('comment_vote.commentId = comment.id'); $select->columns( [ 'commentId' => 'id', 'comment', 'nagetiveVoteCount' => new \Zend\Db\Sql\Expression('?', [$selectSub]) ] ); $statement = $sql->prepareStatementForSqlObject($select); $comments = $statement->execute(); $resultSet = new \Zend\Db\ResultSet\ResultSet(); $resultSet->initialize($comments); return $resultSet->toArray(); }

Find on stackoverflow.

WICHTIG auch (in $selectSub) die Where Klause als String. Würde man es als Array machen, wäre comment.id die Value …es würde nicht interpretiert als ID der TAbelle comment.

]]>
ZF2 controller returned JsonModel https://blog.bitkorn.de/zf2-controller-returned-jsonmodel/ Fri, 25 Aug 2017 10:06:40 +0000 http://blog.t-brieskorn.de/?p=296 Return a

\Zend\View\Model\JsonModel

in controller action.

Tell the view manager to use JsonStrategy:

    'view_manager' => array(
        'template_map' => array(
        ),
        'template_path_stack' => array(
            'MyModule' => __DIR__ . '/../view',
        ),
        'strategies' => array(
            'ViewJsonStrategy', // to call controller action (returned JsonModel) without view file
        ),
    ),

…otherwise it searches for a view file.

]]>
Call to undefined function apache_request_headers() https://blog.bitkorn.de/call-to-undefined-function-apache_request_headers/ Mon, 30 Jan 2017 11:35:17 +0000 http://blog.t-brieskorn.de/?p=231 Falls diese Meldung präsent ist hilft folgendes:

if (!function_exists('apache_request_headers')) {

    function apache_request_headers()
    {
        $arh = array();
        $rx_http = '/\AHTTP_/';
        foreach ($_SERVER as $key => $val) {
            if (preg_match($rx_http, $key)) {
                $arh_key = preg_replace($rx_http, '', $key);
                $rx_matches = array();
                // do some nasty string manipulations to restore the original letter case
                // this should work in most cases
                $rx_matches = explode('_', $arh_key);
                if (count($rx_matches) > 0 and strlen($arh_key) > 2) {
                    foreach ($rx_matches as $ak_key => $ak_val)
                        $rx_matches[$ak_key] = ucfirst($ak_val);
                    $arh_key = implode('-', $rx_matches);
                }
                $arh[$arh_key] = $val;
            }
        }
        return( $arh );
    }

}

Guckst du bei php.net.

Pustekuchen: zuverlässig funktioniert:

In der .htaccess:

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Und dann:

$authorization = filter_var($_SERVER["HTTP_AUTHORIZATION"]);

Guckst du

]]>