Web + Mobile Blog / Berlin http://sebastianviereck.de PHP Freelancer Tue, 06 Aug 2019 14:27:54 +0000 de hourly 1 http://wordpress.org/?v=3.5.1 Gaufrette FTP Adapter rekursiv Verzeichnisse löschen http://sebastianviereck.de/gaufrette-rekursiv-verzeichnisse-loeschen/?utm_source=rss&utm_medium=rss&utm_campaign=gaufrette-rekursiv-verzeichnisse-loeschen http://sebastianviereck.de/gaufrette-rekursiv-verzeichnisse-loeschen/#comments Tue, 06 Aug 2019 14:01:17 +0000 Sebastian Viereck http://sebastianviereck.de/?p=4012-de Weiterlesen ]]> Um mit dem FTP Adapter von Gaufrette rekursiv Dateien und Unterverzeichnisse zu löschen kann man folgenden Trick anwenden um nicht in die Fehlermeldung zu geraten:

ftp_rmdir(): Directory not empty.

1. erst alle Dateien löschen

2. dann die tiefsten Verzeichnisse bis hin zu den obersten löschen:

/**
 * @param Filesystem $fileSystem
 */
function deleteAllFilesInDirectory(Filesystem $fileSystem)
{
    // delete files first, than directories
    foreach ($fileSystem->keys() as $key) {
        if (!$fileSystem->isDirectory($key)) {
            $fileSystem->delete($key);
        }
    }
    $keys = $fileSystem->keys();
    usort($keys, function (string $a, string $b){
        $aCount = substr_count($a, '/');
        $bCount = substr_count($b, '/');
        return $bCount <=> $aCount;
    });
    foreach ($keys as $key) {
        $fileSystem->delete($key);
    }
}
]]>
http://sebastianviereck.de/gaufrette-rekursiv-verzeichnisse-loeschen/feed/ 0
AWS Zeitzone richtig einstellen Amazon Linux 2 und RDS MySQL http://sebastianviereck.de/aws-zeitzone-richtig-einstellen-amazon-linux-2-und-rds-mysql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=aws-zeitzone-richtig-einstellen-amazon-linux-2-und-rds-mysql http://sebastianviereck.de/aws-zeitzone-richtig-einstellen-amazon-linux-2-und-rds-mysql/#comments Fri, 02 Aug 2019 07:18:16 +0000 Sebastian Viereck http://sebastianviereck.de/?p=4005-de Weiterlesen ]]> Damit in den Logs und in der Datenbank die korrekten Zeitstempel der eigenen Zeitzone verwendet werden, muss man

1. Die Systemzeit der EC2 Instanz konfigurieren

sudo ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime

und in der Datei /etc/sysconfig/clock die Zeitzone eintragen:

sudo vi /etc/sysconfig/clock
ZONE="Europe/Berlin"

Danach muss ein Reboot der Instanz erfolgen:

sudo reboot

Die Anpassung kann überprüft werden mit

date

Quelle

2. Die MySQL RDS ParameterGroup anpassen

Setzen des Parameters time_zone auf den Wert Europe/Prague, danach ist ein reboot der RDS Instanz nötig

aws_rds_time_zone

MySQL RDS ParameterGroup time_zone Parameter setzen

Die Anpassung kann überprüft werden mit

SELECT NOW();

Quelle

]]>
http://sebastianviereck.de/aws-zeitzone-richtig-einstellen-amazon-linux-2-und-rds-mysql/feed/ 0
Symfony FosUser Bundle eigene Password Policy anwenden http://sebastianviereck.de/symfony-fosuser-eigene-password-policy/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=symfony-fosuser-eigene-password-policy http://sebastianviereck.de/symfony-fosuser-eigene-password-policy/#comments Tue, 30 Jul 2019 15:58:45 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3999-de Weiterlesen ]]> Um im SonataAdmin mit FosUser Bundle Modul eigene Regeln zu definieren für die Stärke der Passwörter, kann man die validation.xml überschreiben, indem man im Ordner src/Application/Sonata/UserBundle/Resources/config/ eine eigene validation.xml anlegt:

<?xml version="1.0" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
        http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">

    <class name="FOS\UserBundle\Model\User">

        <property name="plainPassword">
            <constraint name="NotBlank">
                <option name="message">fos_user.password.blank</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>ResetPassword</value>
                    <value>ChangePassword</value>
                </option>
            </constraint>
            <constraint name="Length">
                <option name="min">8</option>
                <option name="max">50</option>
                <option name="minMessage">fos_user.password.short</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                    <value>ResetPassword</value>
                    <value>ChangePassword</value>
                </option>
            </constraint>
            <constraint name="Regex">
                <option name="pattern">/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/</option>
                <option name="message">Das Passwort muss mindestens 8 Zeichen haben, eine Zahl, Groß und Kleinschreibung enthalten.</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                    <value>ResetPassword</value>
                    <value>ChangePassword</value>
                </option>
            </constraint>
        </property>
    </class>
</constraint-mapping>
]]>
http://sebastianviereck.de/symfony-fosuser-eigene-password-policy/feed/ 0
Symfony Brute-Force Guard für SonataAdmin erstellen http://sebastianviereck.de/symfony-brute-force-guard-fuer-sonataadmin-erstellen/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=symfony-brute-force-guard-fuer-sonataadmin-erstellen http://sebastianviereck.de/symfony-brute-force-guard-fuer-sonataadmin-erstellen/#comments Tue, 30 Jul 2019 14:24:17 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3997-de Weiterlesen ]]> Für das SonataAdmin Bundle gibt es die Möglichkeit über Symofny Guards eigene Logik in den Loginprozess einzubauen, wie z.B.:

  • Abwehr von Brute Force Angriffen durch eine maximale Anzahl von Login-Versuchen

In dem folgenden Beispiel habe ich einen Guard konfiguriert für den Administrationsbereich, der mitzählt, wie oft sich ein User falsch eingeloggt hat.

Der UserManager muss dann die Logik enthalten, um bei jedem User die Anzahl der Logins mitzuzählen und ihn ggf. auch zu bannen für eine bestimmte Zeit.

<?php

namespace App\Security;

use App\Core\UserManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;

class AdminAuthenticator extends AbstractFormLoginAuthenticator
{

    /**
     * @var UserManager
     */
    protected $userManager;

    /**
     * @var RouterInterface
     */
    protected $router;

    /**
     * Constructor.
     *
     * @param UserManager $userManager
     * @param RouterInterface $router
     */
    public function __construct(
        UserManager $userManager,
        RouterInterface $router
    )
    {
        $this->userManager = $userManager;
        $this->router = $router;
    }

    /**
     * Return the URL to the login page.
     *
     * @return string
     */
    protected function getLoginUrl()
    {
        return $this->router->generate('sonata_user_admin_security_login');
    }

    /**
     * Does the authenticator support the given Request?
     *
     * If this returns false, the authenticator will be skipped.
     *
     * @param Request $request
     *
     * @return bool
     */
    public function supports(Request $request)
    {
        return ($request->getPathInfo() == '/admin/login_check' && $request->getMethod() === 'POST');
    }

    /**
     * Get the authentication credentials from the request and return them
     * as any type (e.g. an associate array).
     *
     * Whatever value you return here will be passed to getUser() and checkCredentials()
     *
     * For example, for a form login, you might:
     *
     *      return [
     *          'username' => $request->request->get('_username'),
     *          'password' => $request->request->get('_password'),
     *      ];
     *
     * Or for an API token that's on a header, you might use:
     *
     *      return ['api_key' => $request->headers->get('X-API-TOKEN')];
     *
     * @param Request $request
     *
     * @return mixed Any non-null value
     *
     * @throws \UnexpectedValueException If null is returned
     */
    public function getCredentials(Request $request)
    {
        return [
               'username' => $request->request->get('_username'),
               'password' => $request->request->get('_password'),
           ];
    }

    /**
     * Return a UserInterface object based on the credentials.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * You may throw an AuthenticationException if you wish. If you return
     * null, then a UsernameNotFoundException is thrown for you.
     *
     * @param mixed $credentials
     * @param UserProviderInterface $userProvider
     *
     * @throws AuthenticationException
     *
     * @return UserInterface|null
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return  $userProvider->loadUserByUsername($credentials['username']);
    }

    /**
     * Returns true if the credentials are valid.
     *
     * If any value other than true is returned, authentication will
     * fail. You may also throw an AuthenticationException if you wish
     * to cause authentication to fail.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * @param mixed $credentials
     * @param UserInterface $user
     *
     * @return bool
     *
     * @throws AuthenticationException
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        if ($this->userManager->checkUserIsAllowedToLogin($user)) {
            return $this->userManager->checkPasswordValid($credentials['password'], $user);
        } else {
            throw new CustomUserMessageAuthenticationException('user is banned for 1 hour');
        }
    }

    /**
     * Called when authentication executed and was successful!
     *
     * This should return the Response sent back to the user, like a
     * RedirectResponse to the last page they visited.
     *
     * If you return null, the current request will continue, and the user
     * will be authenticated. This makes sense, for example, with an API.
     *
     * @param Request $request
     * @param TokenInterface $token
     * @param string $providerKey The provider (i.e. firewall) key
     *
     * @return Response|null
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $user = $token->getUser();
        $this->userManager->onLoginSuccess($user);
    }

    /**
     * @param Request $request
     * @param AuthenticationException $exception
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        if ($exception instanceof BadCredentialsException) {
            $userName = $request->request->get('_username');
            if (!empty($userName)) {
                $user = $this->userManager->getUserByUserName($userName);
                if (!empty($user)) {
                    $this->userManager->incrementPasswordFailedCount($user);
                }
            }
        }

        return parent::onAuthenticationFailure($request, $exception);
    }
}

security.yaml:

security:
  providers:
    fos_userbundle:
      id: App\Security\UserProvider
  admin:
    pattern:            /admin(.*)
    context:            user
    form_login:
        provider:       fos_userbundle
        login_path:     /admin/login
        use_forward:    false
        check_path:     /admin/login_check
        failure_path:   null
        default_target_path: sonata_admin_dashboard
    logout:
        path:           /admin/logout
        target:         /admin/login
    anonymous:          true
    guard:
        authenticators:
            - App\Security\AdminAuthenticator
        provider:       fos_userbundle
access_control:
    # Admin login page needs to be accessed without credential
    - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/, role: ROLE_SONATA_ADMIN }

UserProvider:

<?php

namespace App\Security;

use App\Entity\User;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface as SecurityUserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class UserProvider implements UserProviderInterface
{
    /**
     * @var UserManagerInterface
     */
    protected $userManager;

    /**
     * Constructor.
     *
     * @param UserManagerInterface $userManager
     */
    public function __construct(UserManagerInterface $userManager)
    {
        $this->userManager = $userManager;
    }

    /**
     * {@inheritdoc}
     */
    public function loadUserByUsername($username)
    {
        /** @var User $user */
        $user = $this->findUser($username);

        if (!$user) {
            throw new BadCredentialsException();
        }

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    public function refreshUser(SecurityUserInterface $user)
    {
        if (!$user instanceof UserInterface) {
            throw new UnsupportedUserException(sprintf('Expected an instance of FOS\UserBundle\Model\UserInterface, but got "%s".', get_class($user)));
        }

        if (!$this->supportsClass(get_class($user))) {
            throw new UnsupportedUserException(sprintf('Expected an instance of %s, but got "%s".', $this->userManager->getClass(), get_class($user)));
        }

        if (null === $reloadedUser = $this->userManager->findUserBy(array('id' => $user->getId()))) {
            throw new UsernameNotFoundException(sprintf('User with ID "%s" could not be reloaded.', $user->getId()));
        }

        return $reloadedUser;
    }

    /**
     * {@inheritdoc}
     */
    public function supportsClass($class)
    {
        $userClass = $this->userManager->getClass();

        return $userClass === $class || is_subclass_of($class, $userClass);
    }

    /**
     * Finds a user by username.
     *
     * This method is meant to be an extension point for child classes.
     *
     * @param string $username
     *
     * @return UserInterface|null
     */
    protected function findUser($username)
    {
        return $this->userManager->findUserByUsername($username);
    }
}

services.yaml:

App\Security\AdminAuthenticator:
  - '@App\Core\UserManager'
  - '@router'

App\Security\UserProvider:
  - '@fos_user.user_manager'
]]>
http://sebastianviereck.de/symfony-brute-force-guard-fuer-sonataadmin-erstellen/feed/ 0
letsencrypt AWS http challenge http://sebastianviereck.de/letsencrypt-aws-http-challenge/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=letsencrypt-aws-http-challenge http://sebastianviereck.de/letsencrypt-aws-http-challenge/#comments Thu, 23 May 2019 07:24:37 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3990-de Weiterlesen ]]> Ich hatte das Problem, dass beim Erneuern des Letsencrypt Zertifikates per Cronjob unter Amazon Linux 2

certbot renew --post-hook "systemctl reload httpd"  >> /var/log/certbot.log 2>&1

das SSL Zertifikat nicht erneuert wurde, sondern folgende Fehlermeldung auftrat:

Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',)
Attempting to renew cert (foo.de) from /etc/letsencrypt/renewal/foo.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',). Skipping.

Ich habe dann in Konfiguration geschaut: /etc/letsencrypt/renewal/foo.conf und dort den Authentificator geändert auf apache und den Authentificator Challenge auf HTTP (über den Webserver).

# Options used in the renewal process
[renewalparams]
account = xxx
server = https://acme-v02.api.letsencrypt.org/directory
authenticator = apache
installer = apache
pref_challs = http-01,

Danach war wichtig noch den Port 80 in der Security Group freizuschalten für die Authentifizierung und nach erneuter Eingabe des Befehls

certbot renew

wurde das Zertifikat wurde erfolgreich erneuert.

Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator apache, Installer apache
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for foo.de
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed with reload of apache server; fullchain is
/etc/letsencrypt/live/foo.de/fullchain.pem
]]>
http://sebastianviereck.de/letsencrypt-aws-http-challenge/feed/ 0
Angular ngx-translate TranslateLoader ohne http Modul http://sebastianviereck.de/angular-ngx-translate-translateloader-ohne-http-service/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=angular-ngx-translate-translateloader-ohne-http-service http://sebastianviereck.de/angular-ngx-translate-translateloader-ohne-http-service/#comments Fri, 12 Apr 2019 13:26:23 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3978-de Weiterlesen ]]> Um eine Kompatibilität mit alten Browsern, wie dem Firefox Version <= 5 zu erreichen, kann man das angular http Modul nicht verwenden. Das ngx-translate Module bneötigt einen Loader, der die richtige Sprache lädt per Ajax Request. Dies läst sich auch mit dem XMLHttpRequest und einem Observalbe lösen:

import {Injectable} from '@angular/core';
import {TranslateLoader} from '@ngx-translate/core';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class LanguageLoader implements TranslateLoader {

  getTranslation(lang: string): Observable<any> {
    return Observable.create(function (observer) {
    let url = `./assets/i18n/${lang}.json?=` + new Date().getTime();
    let xhr = new XMLHttpRequest();

      xhr.onreadystatechange = function () {
        if (xhr.readyState == XMLHttpRequest.DONE || xhr.readyState == 4) {
              let response = JSON.parse(xhr.responseText);
              observer.next(response);
              observer.complete();
        }
      }
      xhr.open('GET', url, true);
      xhr.send(null);
    });
  }
}

In der app.module.ts kann man dann den Loader wie folgt einbinden:

TranslateModule.forRoot({
  loader: {provide: TranslateLoader, useClass: LanguageLoader}
})
]]>
http://sebastianviereck.de/angular-ngx-translate-translateloader-ohne-http-service/feed/ 0
PHP Skript als Windows-Dienst ausführen http://sebastianviereck.de/php-skript-als-windows-dienst-ausfuehren/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=php-skript-als-windows-dienst-ausfuehren http://sebastianviereck.de/php-skript-als-windows-dienst-ausfuehren/#comments Sun, 07 Apr 2019 14:28:52 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3956-de Weiterlesen ]]> Unter Windows lassen sich Skripte, die endlos laufen sollen mit PHP mittels eines Dienstes realisieren.

Dies hat den Vorteil, dass der Speicherverbrauch nicht mit der Zeit ins unendliche geht, bei endloser Skriptausführungen und eine Recovery und Restart Funktionalität implementiert werden kann, um den Dienst über lange Zeiträume am Laufen zu halten.

Außerdem erhält der Dienst vom Betriebsystem Events, wenn z.B. eine Shutdown ansteht, um sich rechtzeitig selber beenden zu können und keine korrupten Daten zu produzieren beim Abbruch in einer nicht atomaren Operation.

Um einen Windows Dienst anzulegen benötigt man die win32service PHP-Library.

Diese kann man hier downloaden und in der php.ini einbinden:

extension=php_win32service.dll

Dienst anlegen

Um einen Dienst erstellen und steuern zu können, kann man folgenden Code verwenden und in der Datei win32_service.php speichern:

<?php
//No timeouts, Flush Content immediatly
set_time_limit(0);
ob_implicit_flush();

$serviceName = 'FooCommand';

$serviceAction = $argv[1];

switch ($argv[1]) {
    case  'install': {
        $result = win32_create_service(array(
            'service' => $serviceName,
            'display' => $serviceName,
            'description' => $serviceName,
            'params' => sprintf('%s\win32_run.php', __DIR__),
            'recovery_delay'        => 1000,                                               // Recovery action is executed after 10s
            'recovery_action_1'     => WIN32_SC_ACTION_RESTART,
            'recovery_action_2'     => WIN32_SC_ACTION_RESTART,
            'recovery_action_3'     => WIN32_SC_ACTION_RESTART,
            'recovery_reset_period' => 3600,                                                // Reset the fail counter after 1 hour
        ));
        break;
    }
    case  'uninstall': {
        $result = win32_delete_service($serviceName);
        break;
    }
    case  'stop': {
        $result = win32_stop_service($serviceName);
        break;
    }
    case  'status': {
        $ServiceStatus = win32_query_service_status($serviceName);
        if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_STOPPED ) {
            echo "Service Stopped\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_START_PENDING ) {
            echo "Service Start Pending\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_STOP_PENDING ) {
            echo "Service Stop Pending\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_RUNNING ) {
            echo "Service Running\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_CONTINUE_PENDING ) {
            echo "Service Continue Pending\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_PAUSE_PENDING ) {
            echo "Service Pause Pending\n\n";
        } else if ( $ServiceStatus['CurrentState'] == WIN32_SERVICE_PAUSED ) {
            echo "Service Paused\n\n";
        } else{
            echo "Service Unknown\n\n";
        }
        exit;
    }
    default: {
        throw new Exception('unknow argument');
    }
}

switch($result) {
    case WIN32_ERROR_SERVICE_DOES_NOT_EXIST:
        throw new Exception('service not found');
    case WIN32_SERVICE_ERROR_IGNORE:
        die('service successfully installed/uninstalled');
    case WIN32_ERROR_ACCESS_DENIED:
        throw new Exception('access denied, run command as administrator');
    case WIN32_ERROR_SERVICE_EXISTS:
        throw new Exception('service already exists, uninstall service prior');
    case WIN32_ERROR_SERVICE_MARKED_FOR_DELETE:
        throw new Exception(sprintf('service has been marked for deletion, look here https://stackoverflow.com/questions/20561990/how-to-solve-the-specified-service-has-been-marked-for-deletion-error'));
    case WIN32_SERVICE_START_PENDING:
        throw new Exception('service start is pending');
    case WIN32_ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
        echo 'can not connect to service controller'. PHP_EOL;
        break;
    default:
        # search for hex value of result win32service lib
        debug_zval_dump($result);
}

und ausführen (CMD als Administrator ausführen) mit:

run with: php win32_service.php install
//or
run with: php win32_service.php uninstall
//or
run with: php win32_service.php status
//or
run with: php win32_service.php stop

Dann sollte unter Systemsteuerung -> Dienste der Dienst FooCommand erstellt worden sein, der beim nächsten Windows Start automatisch gestartet wird.

Windows Dienst mit PHP Skript erstellen

Das langlaufende PHP Skript

Das Skript, dass über den Dienst ausgeführt werden soll befindet sich in der Datei: win32_run.php

<?php
set_time_limit(0);
ob_implicit_flush();
$serviceName = 'FooCommand';

$result = win32_start_service_ctrl_dispatcher($serviceName);
if (!$result) {
    die("I'm probably not running under the service control manager");
}
debug_zval_dump($result);
win32_set_service_status(WIN32_SERVICE_START_PENDING);

// Some lengthy process to get this service up and running.

win32_set_service_status(WIN32_SERVICE_RUNNING);

while (WIN32_SERVICE_CONTROL_STOP != win32_get_last_control_message()) {
    exec(sprintf('php %s/script.php', __DIR__));
}

In der script.php kann jetzt der auszuführende endlos Code eingefügt werden:

<?php
echo "hello service";

Konfiguration des Dienstes

Der Dienst kann im Fehlerfall genau konfiguriert werden:

Dienst Konfiguration im Fehlerfall

Debugging des Dienstes

Wenn der Dienst nicht auf Anhieb läuft, kann man eine Fehlermeldung in Systemsteuerung -> Ereignisanzeige mehr Aufschluss geben:

Windows Dienst Ereignisanzeige

]]>
http://sebastianviereck.de/php-skript-als-windows-dienst-ausfuehren/feed/ 0
Windows 10 geplante Aufgabe für PHP Skript anlegen analog Crontab http://sebastianviereck.de/windows-10-geplante-aufgabe-fuer-php-skript-anlegen-analog-crontab/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=windows-10-geplante-aufgabe-fuer-php-skript-anlegen-analog-crontab http://sebastianviereck.de/windows-10-geplante-aufgabe-fuer-php-skript-anlegen-analog-crontab/#comments Sun, 07 Apr 2019 11:15:38 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3949-de Weiterlesen ]]> Für wiederkehrende PHP Jobs kann man den Betriebsystem Scheduler verwenden, unter Linux ist das cron und unter Windows Aufgaben.

Um ein PHP Skript zu starten C:\foo\bar.php mittels der Aufgabenplanung jede Minute kann man unter Windows 10 und darunter wie folgt neue Aufgaben anlegen.

Unter Systemsteuerung -> Aufgabenplanung lassen sich neue Aufgaben verwalten:

Systemsteuerung -> Aufgabenplanung

Aufgabe erstellen – wenn das Skript im Hintergrund ausgeführt werden soll, dann sollte man die Option “Unabhängig von der Bneutzeranmeldung ausführen” ankreuzen

 

Trigger minütlich erstellen, aktiviert unbedingt ankreuzen

auch im Akkubetrieb ausführen

php Skript als Aktion definieren, php muss dafür im PATH eingetragen werden, ansonsten muss der absolute Pfad zur PHP Installation eingetragen werden. In “Starten in” kann ein Verzeichnis angeben werden, in dem der Befehl ausgeführt werden kann.

nach 5 Minuten den Prozess killen

]]>
http://sebastianviereck.de/windows-10-geplante-aufgabe-fuer-php-skript-anlegen-analog-crontab/feed/ 0
Webanwendung zur Vermittlung von IT-Kräften http://sebastianviereck.de/webanwendung-zur-vermittlung-von-it-kraeften/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=webanwendung-zur-vermittlung-von-it-kraeften http://sebastianviereck.de/webanwendung-zur-vermittlung-von-it-kraeften/#comments Sun, 07 Apr 2019 08:59:46 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3946-de Meine Bachelorarbeit zum Download:

Konzept und Entwicklung einer
Webanwendung zur Vermittlung von IT-Kräften

Daraus ist das Startup nerdle.de geworden, welches nach 2 Jahren aufgrund mangelnder Finanzierung eingestellt werden musste, aber mir sehr viel Spaß gemacht hat.

Download .pdf

Download .doc

]]>
http://sebastianviereck.de/webanwendung-zur-vermittlung-von-it-kraeften/feed/ 0
Behavioral Finance und Moving Averages http://sebastianviereck.de/behavioral-finance-und-moving-averages/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=behavioral-finance-und-moving-averages http://sebastianviereck.de/behavioral-finance-und-moving-averages/#comments Sun, 07 Apr 2019 08:52:39 +0000 Sebastian Viereck http://sebastianviereck.de/?p=3941-de Meine Diplomarbeit zum Download

Behavioral Finance und Moving Averages – Anwendbarkeit/Simulation von Aktienmärkten
anhand des 200-Tage-Durchschnitts

Download .pdf

Download .doc

Download Excel Makro

]]>
http://sebastianviereck.de/behavioral-finance-und-moving-averages/feed/ 0