How to Log INFO to separate file in Laravel - php

How to specify a separate file for logging INFO in Laravel 5.1?

Do you want to specifically log info to one log file and another log type to another location? My solution might not help in that case, but could still be useful.
To write a log file to another location, use the method useDailyFiles or useFiles, and then info to log to the log file at the path you just specified. Like so:
Log::useDailyFiles(storage_path().'/logs/name-of-log.log');
Log::info([info to log]);
The first parameter for both methods is the path of the log file (which is created if it doesn't already exist) and for useDailyFiles the second argument is the number of days Laravel will log for before erasing old logs. The default value is unlimited, so in my example I haven't entered a value.

Since Laravel 5.6 you can create your own channel in config\logging.php. If you have upgraded from an older Laravel version you need to create this file (https://laravel.com/docs/5.6/upgrade).
Add this to you channel array in config\logging.php
'your_channel_name' => [
'driver' => 'single',
'path' => storage_path('logs/your_file_name.log'),
],
You can then call any of the 8 logging levels like that:
Illuminate\Support\Facades\Log::channel('your_channel_name')->info('your_message');
The logs will be stored in logs/your_file_name.log

Since Laravel >= 5.6 we can use Log Channels to make it work in a easy way. This allows you to create log channels which can be handled as own log files with own drivers, paths or levels. You just need this few lines to make it work.
Simple add a new channel (choose your channel name, e.g. "command")
config/logging.php:
return [
'channels' => [
'command' => [
'driver' => 'single',
'path' => storage_path('logs/command.log'),
'level' => 'debug',
],
],
];
Log where ever you want by parse the channel name:
Log::channel('command')->info('Something happened!');

A simple logger helper that allows you to log to multiple custom files on the fly. You can also add your custom handler and set the file path.
App\Helper\LogToChannels.php
<?php
/**
* Logger helper to log into different files
*
* #package App\Helpers
* #author Romain Laneuville <romain.laneuville#hotmail.fr>
*/
namespace App\Helpers;
use Monolog\Logger;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;
/**
* Class LogToChannels
*
* #package App\Helpers
*/
class LogToChannels
{
/**
* The LogToChannels channels.
*
* #var Logger[]
*/
protected $channels = [];
/**
* LogToChannels constructor.
*/
public function __construct()
{
}
/**
* #param string $channel The channel to log the record in
* #param int $level The error level
* #param string $message The error message
* #param array $context Optional context arguments
*
* #return bool Whether the record has been processed
*/
public function log(string $channel, int $level, string $message, array $context = []): bool
{
// Add the logger if it doesn't exist
if (!isset($this->channels[$channel])) {
$handler = new StreamHandler(
storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channel . '.log'
);
$handler->setFormatter(new LineFormatter(null, null, true, true));
$this->addChannel($channel, $handler);
}
// LogToChannels the record
return $this->channels[$channel]->{Logger::getLevelName($level)}($message, $context);
}
/**
* Add a channel to log in
*
* #param string $channelName The channel name
* #param HandlerInterface $handler The channel handler
* #param string|null $path The path of the channel file, DEFAULT storage_path()/logs
*
* #throws \Exception When the channel already exists
*/
public function addChannel(string $channelName, HandlerInterface $handler, string $path = null)
{
if (isset($this->channels[$channelName])) {
throw new \Exception('This channel already exists');
}
$this->channels[$channelName] = new Logger($channelName);
$this->channels[$channelName]->pushHandler(
new $handler(
$path === null ?
storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channelName . '.log' :
$path . DIRECTORY_SEPARATOR . $channelName . '.log'
)
);
}
/**
* Adds a log record at the DEBUG level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function debug(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::DEBUG, $message, $context);
}
/**
* Adds a log record at the INFO level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function info(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::INFO, $message, $context);
}
/**
* Adds a log record at the NOTICE level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function notice(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::NOTICE, $message, $context);
}
/**
* Adds a log record at the WARNING level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function warn(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::WARNING, $message, $context);
}
/**
* Adds a log record at the WARNING level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function warning(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::WARNING, $message, $context);
}
/**
* Adds a log record at the ERROR level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function err(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ERROR, $message, $context);
}
/**
* Adds a log record at the ERROR level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function error(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ERROR, $message, $context);
}
/**
* Adds a log record at the CRITICAL level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function crit(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::CRITICAL, $message, $context);
}
/**
* Adds a log record at the CRITICAL level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return Boolean Whether the record has been processed
*/
public function critical(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::CRITICAL, $message, $context);
}
/**
* Adds a log record at the ALERT level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function alert(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ALERT, $message, $context);
}
/**
* Adds a log record at the EMERGENCY level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function emerg(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::EMERGENCY, $message, $context);
}
/**
* Adds a log record at the EMERGENCY level.
*
* #param string $channel The channel name
* #param string $message The log message
* #param array $context The log context
*
* #return bool Whether the record has been processed
*/
public function emergency(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::EMERGENCY, $message, $context);
}
}
App\Providers\LogToChannelsServiceProvider.php
<?php
/**
* Logger service provider to be abled to log in different files
*
* #package App\Providers
* #author Romain Laneuville <romain.laneuville#hotmail.fr>
*/
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Helpers\LogToChannels;
/**
* Class LogToChannelsServiceProvider
*
* #package App\Providers
*/
class LogToChannelsServiceProvider extends ServiceProvider
{
/**
* Initialize the logger
*
* #return void
*/
public function register()
{
$this->app->singleton('App\Helpers\LogToChannels', function () {
return new LogToChannels();
});
}
}
config\app.php (add the service provider)
// Register Service Providers
$app->register(App\Providers\LogToChannelsServiceProvider::class);
Then anywhere in your app you can call using dependency injection (add the class in your constructor and bind it to a log class attribute)
$this->log->info('logger_name', 'Log message');
$this->log->error('other_logger_name', 'Log message', $someContext);
You can even customize your logger output by calling
$this->log->addChannel('channel_name', $customHandler);
And it will be accessible when you will call its name anywhere in your app.

If you would like add another monolog handler, you may use the application's configureMonologUsing method.
Place a call to this method in bootstrap/app.php file right before the $app variable is returned:
$app->configureMonologUsing(function($monolog) {
$monolog->pushHandler(new StreamHandler('path/to/info.log', Logger::INFO, false)); // false value as third argument to disable bubbling up the stack
});
return $app;

If you want to separate only a set of logs you can do it using useFiles method, as follows:
\Log::useFiles(storage_path() . '/logs/your_custom_log_file.log');
❗❗❗ But keep in mind that useFiles() does not overwrite the default log handler, so the logs will be added to laravel.log too.
In Laravel 5.4 when I need to create a log in a separate file, I fall back to file_put_contents function:
$myLogInfo = "...";
file_put_contents(
storage_path() . '/logs/your_custom_log_file.log',
$myLogInfo . PHP_EOL,
FILE_APPEND | LOCK_EX
);

Related

I am facing problem (csrf_token' URL query argument is invalid) with flag module at Drupal 8

I generated flag link
$flag_link = [
'#lazy_builder' => ['flag.link_builder:build', [
$product->getEntityTypeId(),
$product->id(),
'product_like',
]],
'#create_placeholder' => TRUE,
];
Flag link is generated successfully. But while I click flag link , I got error message as response
{message: "'csrf_token' URL query argument is invalid."}
message: "'csrf_token' URL query argument is invalid."
I found a temporary solution. Not sure if this is a bug in Flag that needs to be addressed by the module maintainers, or if this is working as intended, since this is a REST response, and not a typical Drupal call for a view or display mode.
In the ModuleRestResource.php file (In my case, the ModuleRestResource.php file is located at:
{{DRUPAL_ROOT}}/web/modules/custom/{{Module_Name}}/src/Plugin/rest/resource/{{Module_Name}}RestResource.php):
use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityInterface;
use Drupal\flag\FlagService;
use Drupal\Core\Render\RenderContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class ModuleRestResource extends ResourceBase {
/**
* A current user instance.
*
* #var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* #var $entityTypeManager \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/**
* #var \Drupal\flag\FlagService
*/
protected $flagService;
/**
* #var Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfService;
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->logger = $container->get('logger.factory')->get('module');
$instance->currentUser = $container->get('current_user');
$instance->entityTypeManager = $container->get('entity_type.manager');
$instance->flagService = $container->get('flag');
$instance->csrfService = $container->get('csrf_token');
return $instance;
}
/**
* Responds to GET requests.
*
* #param string $payload
*
* #return \Drupal\rest\ResourceResponse
* The HTTP response object.
*
* #throws \Symfony\Component\HttpKernel\Exception\HttpException
* Throws exception expected.
*/
public function get($payload) {
// You must to implement the logic of your REST Resource here.
// Use current user after pass authentication to validate access.
if (!$this->currentUser->hasPermission('access content')) {
throw new AccessDeniedHttpException();
}
if (!is_numeric($payload)) {
throw new BadRequestHttpException();
}
/*
* This is the object that will be returned with the node details.
*/
$obj = new \stdClass();
// First load our node.
/**
* #var \Drupal\Core\Entity\EntityInterface
*/
$node = $this->entityTypeManager->getStorage('node')->load($payload);
/**
* FIX STARTS HERE !!!!!
*/
/**
* Because we are rending code early in the process, we need to wrap in executeInRenderContext
*/
$render_context = new RenderContext();
$fl = \Drupal::service('renderer')->executeInRenderContext($render_context, function() use ($node, $payload) {
/**
* Get the flag we need and check if the selected node has been flagged by the current user
*
* Set the path to create a token. This is the value that is missing by default that creates an
* invalid CSRF Token. Important to note that the leading slash should be left off for token generation
* and then added to to the links href attribute
*
*/
$flag = $this->flagService->getFlagById('bookmark');
$is_flagged = (bool) $this->flagService->getEntityFlaggings($flag, $node, \Drupal::currentUser() );
$path = 'flag/'. ($is_flagged ? 'un' : '') .'flag/bookmark/' . $node->id();
$token = $this->csrfService->get($path);
$flag_link = $flag->getLinkTypePlugin()->getAsFlagLink($flag, $node);
$flag_link['#attributes']['href'] = '/' . $path . '?destination&token=' . $token;
/**
* Render the link into HTML
*/
return \Drupal::service('renderer')->render($flag_link);
});
/**
* This is required to bubble metadata
*/
if (!$render_context->isEmpty()) {
$bubbleable_metadata = $render_context->pop();
\Drupal\Core\Render\BubbleableMetadata::createFromObject($fl)
->merge($bubbleable_metadata);
}
/*
* !!!!! FIX ENDS HERE !!!!!
*/
$obj->flag_link = $fl;
return new ResourceResponse((array)$obj, 200);
}
}
Anyone who can get module maintainers to address this would be nice.

Laravel Package laravel-referer-redirector not working

I am trying to install laravel-referer-redirector taking referance from the url listed below. But its not working.
http://www.wallogit.com/composer/type/3.library/appstract/135193.laravel-referer-redirector
Installation goes well but after that when running the below command it throws error
php artisan vendor:publish --provider="Appstract\RefererRedirector\RefererRedirectorServiceProvider"
Error =>
php artisan vendor:publish
--provider="Appstract\RefererRedirector\RefererRedirectorServiceProvider"
{"error":{"type":"Symfony\Component\Debug\Exception\FatalThrowableError","message":"Class
'Appstract\RefererRedirector\RefererRedirectorServiceProvider::class'
not
found","file":"/var/www/html/wyskill/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php","line":157}}
ProvderRepository.php =>
<?php namespace Illuminate\Foundation;
use Illuminate\Filesystem\Filesystem;
class ProviderRepository {
/**
* The filesystem instance.
*
* #var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* The path to the manifest.
*
* #var string
*/
protected $manifestPath;
/**
* Default manifest structure.
*
* #var array
*/
protected $default = array('when' => array());
/**
* Create a new service repository instance.
*
* #param \Illuminate\Filesystem\Filesystem $files
* #param string $manifestPath
* #return void
*/
public function __construct(Filesystem $files, $manifestPath)
{
$this->files = $files;
$this->manifestPath = $manifestPath;
}
/**
* Register the application service providers.
*
* #param \Illuminate\Foundation\Application $app
* #param array $providers
* #return void
*/
public function load(Application $app, array $providers)
{
$manifest = $this->loadManifest();
// First we will load the service manifest, which contains information on all
// service providers registered with the application and which services it
// provides. This is used to know which services are "deferred" loaders.
if ($this->shouldRecompile($manifest, $providers))
{
$manifest = $this->compileManifest($app, $providers);
}
// If the application is running in the console, we will not lazy load any of
// the service providers. This is mainly because it's not as necessary for
// performance and also so any provided Artisan commands get registered.
if ($app->runningInConsole())
{
$manifest['eager'] = $manifest['providers'];
}
// Next, we will register events to load the providers for each of the events
// that it has requested. This allows the service provider to defer itself
// while still getting automatically loaded when a certain event occurs.
foreach ($manifest['when'] as $provider => $events)
{
$this->registerLoadEvents($app, $provider, $events);
}
// We will go ahead and register all of the eagerly loaded providers with the
// application so their services can be registered with the application as
// a provided service. Then we will set the deferred service list on it.
foreach ($manifest['eager'] as $provider)
{
$app->register($this->createProvider($app, $provider));
}
$app->setDeferredServices($manifest['deferred']);
}
/**
* Register the load events for the given provider.
*
* #param \Illuminate\Foundation\Application $app
* #param string $provider
* #param array $events
* #return void
*/
protected function registerLoadEvents(Application $app, $provider, array $events)
{
if (count($events) < 1) return;
$app->make('events')->listen($events, function() use ($app, $provider)
{
$app->register($provider);
});
}
/**
* Compile the application manifest file.
*
* #param \Illuminate\Foundation\Application $app
* #param array $providers
* #return array
*/
protected function compileManifest(Application $app, $providers)
{
// The service manifest should contain a list of all of the providers for
// the application so we can compare it on each request to the service
// and determine if the manifest should be recompiled or is current.
$manifest = $this->freshManifest($providers);
foreach ($providers as $provider)
{
$instance = $this->createProvider($app, $provider);
// When recompiling the service manifest, we will spin through each of the
// providers and check if it's a deferred provider or not. If so we'll
// add it's provided services to the manifest and note the provider.
if ($instance->isDeferred())
{
foreach ($instance->provides() as $service)
{
$manifest['deferred'][$service] = $provider;
}
$manifest['when'][$provider] = $instance->when();
}
// If the service providers are not deferred, we will simply add it to an
// of eagerly loaded providers that will be registered with the app on
// each request to the applications instead of being lazy loaded in.
else
{
$manifest['eager'][] = $provider;
}
}
return $this->writeManifest($manifest);
}
/**
* Create a new provider instance.
*
* #param \Illuminate\Foundation\Application $app
* #param string $provider
* #return \Illuminate\Support\ServiceProvider
*/
public function createProvider(Application $app, $provider)
{
return new $provider($app);
}
/**
* Determine if the manifest should be compiled.
*
* #param array $manifest
* #param array $providers
* #return bool
*/
public function shouldRecompile($manifest, $providers)
{
return is_null($manifest) || $manifest['providers'] != $providers;
}
/**
* Load the service provider manifest JSON file.
*
* #return array
*/
public function loadManifest()
{
$path = $this->manifestPath.'/services.json';
// The service manifest is a file containing a JSON representation of every
// service provided by the application and whether its provider is using
// deferred loading or should be eagerly loaded on each request to us.
if ($this->files->exists($path))
{
$manifest = json_decode($this->files->get($path), true);
return array_merge($this->default, $manifest);
}
}
/**
* Write the service manifest file to disk.
*
* #param array $manifest
* #return array
*/
public function writeManifest($manifest)
{
$path = $this->manifestPath.'/services.json';
$this->files->put($path, json_encode($manifest, JSON_PRETTY_PRINT));
return $manifest;
}
/**
* Create a fresh manifest array.
*
* #param array $providers
* #return array
*/
protected function freshManifest(array $providers)
{
list($eager, $deferred) = array(array(), array());
return compact('providers', 'eager', 'deferred');
}
/**
* Get the filesystem instance.
*
* #return \Illuminate\Filesystem\Filesystem
*/
public function getFilesystem()
{
return $this->files;
}
}
Please guide accordingly. I am using Laravel 4.2.

Drupal 8.3 Custom Rest POST Error BadRequestHttpException: The type link relation must be specified

I have try to create a Custom REST POST plugin in my Drupal 8.3.2 for get an external JSON and then create an article from that.
I have follow that guide: How to create Custom Rest Resources for POST methods in Drupal 8
And this is my code:
<?php
namespace Drupal\import_json_test\Plugin\rest\resource;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\node\Entity\Node;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Psr\Log\LoggerInterface;
/**
* Provides a resource to get view modes by entity and bundle.
*
* #RestResource(
* id = "tio_rest_json_source",
* label = #Translation("Tio rest json source"),
* serialization_class = "Drupal\node\Entity\Node",
* uri_paths = {
* "canonical" = "/api/custom/",
* "https://www.drupal.org/link-relations/create" = "/api/custom"
* }
* )
*/
class TioRestJsonSource extends ResourceBase {
/**
* A current user instance.
*
* #var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* Constructs a new TioRestJsonSource object.
*
* #param array $configuration
* A configuration array containing information about the plugin
instance.
* #param string $plugin_id
* The plugin_id for the plugin instance.
* #param mixed $plugin_definition
* The plugin implementation definition.
* #param array $serializer_formats
* The available serialization formats.
* #param \Psr\Log\LoggerInterface $logger
* A logger instance.
* #param \Drupal\Core\Session\AccountProxyInterface $current_user
* A current user instance.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
array $serializer_formats,
LoggerInterface $logger,
AccountProxyInterface $current_user) {
parent::__construct($configuration, $plugin_id,
$plugin_definition, $serializer_formats, $logger);
$this->currentUser = $current_user;
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array
$configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('import_json_test'),
$container->get('current_user')
);
}
/**
* Responds to POST requests.
*
* Returns a list of bundles for specified entity.
*
* #param $data
*
* #param $node_type
*
* #return \Drupal\rest\ResourceResponse
*
* #throws \Symfony\Component\HttpKernel\Exception\HttpException
* Throws exception expected.
*/
public function post($node_type, $data) {
// You must to implement the logic of your REST Resource here.
// Use current user after pass authentication to validate access.
if (!$this->currentUser->hasPermission('access content')) {
throw new AccessDeniedHttpException();
}
$node = Node::create(
array(
'type' => $node_type,
'title' => $data->title->value,
'body' => [
'summary' => '',
'value' => $data->body->value,
'format' => 'full_html',
],
)
);
$node->save();
return new ResourceResponse($node);
}
}
Now if i try to test this without passing a payload and modifing the return value in this way:
return new ResourceResponse(array('test'=>'OK'));
It's working!
But if i send a custom payload like this using my custom code above:
{
"title": [{
"value": "Test Article custom rest"
}],
"type": [{
"target_id": "article"
}],
"body": [{"value": "article test custom"}]
}
I recieve a 400 Error with: Symfony\Component\HttpKernel\Exception\BadRequestHttpException: The type link relation must be specified. in Drupal\rest\RequestHandler->handle() (line 103 of core/modules/rest/src/RequestHandler.php).
What's going Wrong?
Thx.
I have find a solution:
I have removed the annotation:
* serialization_class = "Drupal\node\Entity\Node",
Then i take care just for data in my post function:
/**
* Responds to POST requests.
*
* Returns a list of bundles for specified entity.
*
* #param $data
*
*
* #return \Drupal\rest\ResourceResponse
*
* #throws \Symfony\Component\HttpKernel\Exception\HttpException
* Throws exception expected.
*/
public function post($data) {
// You must to implement the logic of your REST Resource here.
// Use current user after pass authentication to validate access.
if (!$this->currentUser->hasPermission('access content')) {
throw new AccessDeniedHttpException();
}
return new ResourceResponse(var_dump($data));
The important thing is, when you use postman for example, is to add an header with Content-Type -> application/json:
Instead of Content-Type -> application/hal+json
With this configuration i can post any type of JSON and then manage it as i prefer.
Bye!

How to make a custom redirect function (extending redirect)? - Laravel 5.1

I'm trying to make a custom redirect function. I have created a custom route function in a new file (helpers.php) that works fine:
if (! function_exists('cms_route')) {
/**
* Generate a URL to a named route with predefined cms path.
*
* #param string $name
* #param array $parameters
* #param bool $absolute
* #param \Illuminate\Routing\Route $route
* #return string
*/
function cms_route($name, $parameters = [], $absolute = true, $route = null)
{
return app('url')->route(config('constants.cms_path').'.'.$name, $parameters, $absolute, $route);
}
}
I'm trying to call this function with redirect()->cms_route('name') instead of redirect()->route('name')
So when the cms path is changed everything keeps working.
How would I accomplish this?
Added as quick fix:
if (! function_exists('cms_redirect')) {
/**
* Get an instance of the redirector.
*
* #param string $name
* #param array $parameters
* #param bool $absolute
* #param \Illuminate\Routing\Route $route
* #return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
function cms_redirect($name, $parameters = [])
{
return redirect()->route(config('constants.cms_path').'.'.$name, $parameters);
}
}

Extract Via header branch token from SIP message with regex

I'm trying to extract branch=z9hG4bKlmrltg10b801lgkf0681.1 from the Via: header of a SIP message. Here is the PHP code I have tried:
preg_match('/.branch=.* + From:/', $msg, $result)
And here is the value of $msg:
"INVITE sip:3310094#mediastream.voip.cabletel.net:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.50.240:5060;branch=z9hG4bKlmrltg10b801lgkf0681.1
From: DEATON JEANETTE<sip:9123840782#mediastream.voip.cabletel.net:5060>;tag=SDg7j0c01-959bf958-d8f0f4ea-13c4-50029-140b-4d106390-140b"
How can I correct my regex to make this work?
Please parse your SIP messages properly. I find it highly unlikely that you only want the branch ID, you almost certainly want other information about the transaction other than a pseudo-call ID. SIP messages follow a standardised message format used by several other protocols (including HTTP ;-) ) and there are several libraries out there designed for parsing this message format.
To demonstrate how relatively simple and considerably more powerful this is, lets first take a look at the RFC822 message parser classes that I wrote some time ago (although they have been recently improved and updated). These can be used for parsing emails, I also have a few simple HTTP message parser classes that extend from these:
<?php
/**
* Class representing the basic RFC822 message format
*
* #author Chris Wright
* #version 1.1
*/
class RFC822Message
{
/**
* #var array Collection of headers from the message
*/
protected $headers = array();
/**
* #var string The message body
*/
protected $body;
/**
* Constructor
*
* #param array $headers Collection of headers from the message
* #param string $body The message body
*/
public function __construct($headers, $body)
{
$this->headers = $headers;
$this->body = $body;
}
/**
* Get the value of a header from the message
*
* #param string $name The name of the header
*
* #return array The value(s) of the header from the request
*/
public function getHeader($name)
{
$name = strtolower(trim($name));
return isset($this->headers[$name]) ? $this->headers[$name] : null;
}
/**
* Get the message body
*
* #return string The message body
*/
public function getBody()
{
return $this->body;
}
}
/**
* Factory which makes RFC822 message objects
*
* #author Chris Wright
* #version 1.1
*/
class RFC822MessageFactory
{
/**
* Create a new RFC822 message object
*
* #param array $headers The request headers
* #param string $body The request body
*/
public function create($headers, $body)
{
return new RFC822Message($headers, $body);
}
}
/**
* Parser which creates RFC822 message objects from strings
*
* #author Chris Wright
* #version 1.2
*/
class RFC822MessageParser
{
/**
* #var RFC822MessageFactory Factory which makes RFC822 message objects
*/
protected $messageFactory;
/**
* Constructor
*
* #param RFC822MessageFactory $messageFactory Factory which makes RFC822 message objects
*/
public function __construct(RFC822MessageFactory $messageFactory)
{
$this->messageFactory = $messageFactory;
}
/**
* Split a message into head and body sections
*
* #param string $message The message string
*
* #return array Head at index 0, body at index 1
*/
protected function splitHeadFromBody($message)
{
$parts = preg_split('/\r?\n\r?\n/', ltrim($message), 2);
return array(
$parts[0],
isset($parts[1]) ? $parts[1] : null
);
}
/**
* Parse the header section into a normalized array
*
* #param string $head The message head section
*
* #return array The parsed headers
*/
protected function parseHeaders($head)
{
$expr =
'!
^
([^()<>#,;:\\"/[\]?={} \t]+) # Header name
[ \t]*:[ \t]*
(
(?:
(?: # First line of value
(?:"(?:[^"\\\\]|\\\\.)*"|\S+) # Quoted string or unquoted token
[ \t]* # LWS
)*
(?: # Folded lines
\r?\n
[ \t]+ # ...must begin with LWS
(?:
(?:"(?:[^"\\\\]|\\\\.)*"|\S+) # ...followed by quoted string or unquoted tokens
[ \t]* # ...and maybe some more LWS
)*
)*
)?
)
\r?$
!smx';
preg_match_all($expr, $head, $matches);
$headers = array();
for ($i = 0; isset($matches[0][$i]); $i++) {
$name = strtolower($matches[1][$i]);
if (!isset($headers[$name])) {
$headers[$name] = array();
}
$value = preg_replace('/\s+("(?:[^"\\\\]|\\\\.)*"|\S+)/s', ' $1', $matches[2][$i]);
$headers[$name][] = $value;
}
return $headers;
}
/**
* Create a message object from a string
*
* #param string $message The message string
*
* #return RFC822Message The parsed message object
*/
public function parseMessage($message)
{
list($head, $body) = $this->splitHeadFromBody($message);
$headers = $this->parseHeaders($head);
return $this->requestFactory->create($headers, $body);
}
}
Nothing particularly scary there if you ignore the terrifying regex for parsing the headers :-P - seriously though, these classes can be used unmodified for parsing the header sections of emails, the basis of RFC822 formatted messages.
SIP models itself on HTTP, so with a couple of fairly simple modifications to the HTTP message parsing classes we can easily adapt them to SIP. Let's take a look at those - in these classes I have (more or less) done a search for HTTP and replaced it with SIP:
<?php
/**
* Abstract class representing a SIP message
*
* #author Chris Wright
* #version 1.0
*/
abstract class SIPMessage extends RFC822Message
{
/**
* #var string The message protocol version
*/
protected $version;
/**
* Constructor
*
* #param array $headers Collection of headers from the message
* #param string $body The message body
* #param string $version The message protocol version
*/
public function __construct($headers, $body, $version)
{
parent::__construct($headers, $body);
$this->version = $version;
}
/**
* Get the message protocol version
*
* #return string The message protocol version
*/
public function getVersion()
{
return $this->version;
}
}
/**
* Class representing a SIP request message
*
* #author Chris Wright
* #version 1.0
*/
class SIPRequest extends SIPMessage
{
/**
* #var string The request method
*/
private $method;
/**
* #var string The request URI
*/
private $uri;
/**
* Constructor
*
* #param array $headers The request headers
* #param string $body The request body
* #param string $version The request protocol version
* #param string $method The request method
* #param string $uri The request URI
*/
public function __construct($headers, $body, $version, $method, $uri)
{
parent::__construct($headers, $body, $version);
$this->method = $method;
$this->uri = $uri;
}
/**
* Get the request method
*
* #return string The request method
*/
public function getMethod()
{
return $this->method;
}
/**
* Get the request URI
*
* #return string The request URI
*/
public function getURI()
{
return $this->uri;
}
}
/**
* Class representing a SIP response message
*
* #author Chris Wright
* #version 1.0
*/
class SIPResponse extends SIPMessage
{
/**
* #var int The response code
*/
private $code;
/**
* #var string The response message
*/
private $message;
/**
* Constructor
*
* #param array $headers The request headers
* #param string $body The request body
* #param string $version The request protocol version
* #param int $code The response code
* #param string $message The response message
*/
public function __construct($headers, $body, $version, $code, $message)
{
parent::__construct($headers, $body, $version);
$this->code = $code;
$this->message = $message;
}
/**
* Get the response code
*
* #return int The response code
*/
public function getCode()
{
return $this->code;
}
/**
* Get the response message
*
* #return string The response message
*/
public function getMessage()
{
return $this->message;
}
}
/**
* Factory which makes SIP request objects
*
* #author Chris Wright
* #version 1.0
*/
class SIPRequestFactory extends RFC822MessageFactory
{
/**
* Create a new SIP request object
*
* The last 3 arguments of this method are only optional to prevent PHP from triggering
* an E_STRICT at compile time. IMO this particular error is itself an error on the part
* of the PHP designers, and I don't feel bad about about this workaround, even if it
* does mean the signature is technically wrong. It is the lesser of two evils.
*
* #param array $headers The request headers
* #param string $body The request body
* #param string $version The request protocol version
* #param string $method The request method
* #param string $uri The request URI
*/
public function create($headers, $body, $version = null, $method = null, $uri = null)
{
return new SIPRequest($headers, $body, $version, $method, $uri);
}
}
/**
* Factory which makes SIP response objects
*
* #author Chris Wright
* #version 1.0
*/
class SIPResponseFactory extends RFC822MessageFactory
{
/**
* Create a new SIP response object
*
* The last 3 arguments of this method are only optional to prevent PHP from triggering
* an E_STRICT at compile time. IMO this particular error is itself an error on the part
* of the PHP designers, and I don't feel bad about about this workaround, even if it
* does mean the signature is technically wrong. It is the lesser of two evils.
*
* #param array $headers The response headers
* #param string $body The response body
* #param string $version The response protocol version
* #param int $code The response code
* #param string $message The response message
*/
public function create($headers, $body, $version = null, $code = null, $message = null)
{
return new SIPResponse($headers, $body, $version, $code, $message);
}
}
/**
* Parser which creates SIP message objects from strings
*
* #author Chris Wright
* #version 1.0
*/
class SIPMessageParser extends RFC822MessageParser
{
/**
* #var SIPRequestFactory Factory which makes SIP request objects
*/
private $requestFactory;
/**
* #var SIPResponseFactory Factory which makes SIP response objects
*/
private $responseFactory;
/**
* Constructor
*
* #param SIPRequestFactory $requestFactory Factory which makes SIP request objects
* #param SIPResponseFactory $responseFactory Factory which makes SIP response objects
*/
public function __construct(SIPRequestFactory $requestFactory, SIPResponseFactory $responseFactory)
{
$this->requestFactory = $requestFactory;
$this->responseFactory = $responseFactory;
}
/**
* Remove the request line from the message and parse into tokens
*
* #param string $head The message head section
*
* #return array The parsed request line at index 0, the remainder of the message at index 1
*
* #throws \DomainException When the request line of the message is invalid
*/
private function removeAndParseRequestLine($head)
{
// Note: this method forgives a couple of minor standards violations, mostly for benefit
// of some older Polycom phones and for Voispeed, who seem to make stuff up as they go
// along. It also treats the whole line as case-insensitive even though methods are
// officially case-sensitive, because having two different casings of the same verb mean
// different things makes no sense semantically or implementationally.
// Side note, from RFC3261:
// > The SIP-Version string is case-insensitive, but implementations MUST send upper-case
// Wat. Go home Rosenberg, et. al., you're drunk.
$parts = preg_split('/\r?\n/', $head, 2);
$expr =
'#^
(?:
([^\r\n \t]+) [ \t]+ ([^\r\n \t]+) [ \t]+ SIP/(\d+\.\d+) # request
|
SIP/(\d+\.\d+) [ \t]+ (\d+) [ \t]+ ([^\r\n]+) # response
)
$#ix';
if (!preg_match($expr, $parts[0], $match)) {
throw new \DomainException('Request-Line of the message is invalid');
}
if (empty($match[4])) { // request
$requestLine = array(
'method' => strtoupper($match[1]),
'uri' => $match[2],
'version' => $match[3]
);
} else { // response
$requestLine = array(
'version' => $match[4],
'code' => (int) $match[5],
'message' => $match[6]
);
}
return array(
$requestLine,
isset($parts[1]) ? $parts[1] : ''
);
}
/**
* Create the appropriate message object from a string
*
* #param string $message The message string
*
* #return SIPRequest|SIPResponse The parsed message object
*
* #throws \DomainException When the message string is not valid SIP message
*/
public function parseMessage($message)
{
list($head, $body) = $this->splitHeadFromBody($message);
list($requestLine, $head) = $this->removeAndParseRequestLine($head);
$headers = $this->parseHeaders($head);
if (isset($requestLine['uri'])) {
return $this->requestFactory->create(
$headers,
$body,
$requestLine['version'],
$requestLine['method'],
$requestLine['uri']
);
} else {
return $this->responseFactory->create(
$headers,
$body,
$requestLine['version'],
$requestLine['code'],
$requestLine['message']
);
}
}
}
Seems like a lot of code just for extracting one header value, doesn't it? Well, it is. But that's not just what it does. It parses the whole message into a data structure that provides easy access to any number of bits of information, allowing for (more or less) anything that the standards can throw at you.
So, lets take a look at how you would actually use it:
// First we create a parser object
$messageParser = new SIPMessageParser(
new SIPRequestFactory,
new SIPResponseFactory
);
// Parse the message into an object
try {
$message = $messageParser->parseMessage($msg);
} catch (Exception $e) {
// The message parsing failed, handle the error here
}
// Get the value of the Via: header
$via = $message->getHeader('Via');
// SIP is irritatingly non-specific about the format of branch IDs. This
// expression matches either a quoted string or an unquoted token, which is
// about all that you can say for sure about arbitrary implementations.
$expr = '/branch=(?:"((?:[^"\\\\]|\\\\.)*)"|(.+?)(?:\s|;|$))/i';
// NB: this assumes the message has a single Via: header and a single branch ID.
// In reality this is rarely the case for messages that are received, although
// it is usually the case for messages before they are sent.
if (!preg_match($expr, $via[0], $matches)) {
// The Via: header does not contain a branch ID, handle this error
}
$branchId = !empty($matches[2]) ? $matches[2] : $matches[1];
var_dump($branchId);
See it working
This answer is almost certainly massively overkill for the problem at hand. However, I would regard it as the right way to approach this problem.
preg_match('/branch=.*/i', $msg, $result);
print_r($result);
Will yield something like:
Array
(
[0] => branch=z9hG4bKlmrltg10b801lgkf0681.1
)
Try this
$str = "INVITE sip:3310094#mediastream.voip.cabletel.net:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.50.240:5060;branch=z9hG4bKlmrltg10b801lgkf0681.1
From: DEATON JEANETTE<sip:9123840782#mediastream.voip.cabletel.net:5060>;tag=SDg7j0c01-959bf958-d8f0f4ea-13c4-50029-140b-4d106390-140b";
preg_match('/branch=(.*)From:/i', $str, $output);
print_r( $output );
Try this regex. It checks if there's a space or newline after the branch code. The result you want is always stored in $output[0]
$str = "INVITE sip:3310094#mediastream.voip.cabletel.net:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.50.240:5060;branch=z9hG4bKlmrltg10b801lgkf0681.1 From: DEATON JEANETTE<sip:9123840782#mediastream.voip.cabletel.net:5060>;tag=SDg7j0c01-959bf958-d8f0f4ea-13c4-50029-140b-4d106390-140b";
preg_match('/(branch=.*)( |\r\n)/', $str, $output);
print_r( $output ); // $output[0] is what you need
Example:
http://codepad.viper-7.com/Gj0lWD
You can use a look-ahead assertion like this:
preg_match_all('/.branch=(.*?)(?=^\S|\Z)/sm', $msg, $matches);
Here, (?=^\S|\Z) asserts a new line followed by a non-space (aka folding header) or end-of-subject. This is where the match should end.
Or just match branch= until the end of line:
preg_match_all('/.branch=(.*)/m', $msg, $matches);
Works for headers that are not folded.
See also: Basic rules of HTTP headers

Categories