I am trying to use papertrail as the logging channel in my laravel 8.X application. Whenever I use the following statement, it is throwing an exception
Log::channel('papertrail')->info('Something happened!');
socket_sendto(): unable to write to socket [1]: Operation not permitted
vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php:52
public function write($line, $header = "")
{
$this->send($this->assembleMessage($line, $header));
}
public function close(): void
{
if (is_resource($this->socket)) {
socket_close($this->socket);
$this->socket = null;
}
}
protected function send(string $chunk): void
{
if (!is_resource($this->socket)) {
throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore');
}
socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port);
}
protected function assembleMessage(string $line, string $header): string
{
$chunkSize = static::DATAGRAM_MAX_LENGTH - strlen($header);
return $header . Utils::substr($line, 0, $chunkSize);
}
}
Please advice.
You are getting this error because the log info you are sending is too big. (It probably works most of the time, but some larger errors are too big).
Laravel is using by default UDP to send the log to Papertrail.
The Papertrail accepts logs that UDP packets of up to 1024 bytes / TCP that are up to 100,000 bytes (~100 KB) long.
https://www.papertrail.com/help/troubleshooting-remote-syslog-reachability/#message-length
A solution is to use TCP. Since Laravel 9 there is an env variable to override this:
LOG_PAPERTRAIL_HANDLER = SocketHandler::class logging.php
For older versions of Laravel, you can override the config/logging.php to use the SocketHandler::class in stead of the SyslogUdpHandler::class.
Remark: not all shared hosting providers allow to make a TLS connection.
I am looking for a simple code to create a WebSocket server. I found phpwebsockets but it is outdated now and doesn't support the newest protocol. I tried updating it myself but it doesn't seem to work.
#!/php -q
<?php /* >php -q server.php */
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$master = WebSocket("localhost",12345);
$sockets = array($master);
$users = array();
$debug = false;
while(true){
$changed = $sockets;
socket_select($changed,$write=NULL,$except=NULL,NULL);
foreach($changed as $socket){
if($socket==$master){
$client=socket_accept($master);
if($client<0){ console("socket_accept() failed"); continue; }
else{ connect($client); }
}
else{
$bytes = #socket_recv($socket,$buffer,2048,0);
if($bytes==0){ disconnect($socket); }
else{
$user = getuserbysocket($socket);
if(!$user->handshake){ dohandshake($user,$buffer); }
else{ process($user,$buffer); }
}
}
}
}
//---------------------------------------------------------------
function process($user,$msg){
$action = unwrap($msg);
say("< ".$action);
switch($action){
case "hello" : send($user->socket,"hello human"); break;
case "hi" : send($user->socket,"zup human"); break;
case "name" : send($user->socket,"my name is Multivac, silly I know"); break;
case "age" : send($user->socket,"I am older than time itself"); break;
case "date" : send($user->socket,"today is ".date("Y.m.d")); break;
case "time" : send($user->socket,"server time is ".date("H:i:s")); break;
case "thanks": send($user->socket,"you're welcome"); break;
case "bye" : send($user->socket,"bye"); break;
default : send($user->socket,$action." not understood"); break;
}
}
function send($client,$msg){
say("> ".$msg);
$msg = wrap($msg);
socket_write($client,$msg,strlen($msg));
}
function WebSocket($address,$port){
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master,20) or die("socket_listen() failed");
echo "Server Started : ".date('Y-m-d H:i:s')."\n";
echo "Master socket : ".$master."\n";
echo "Listening on : ".$address." port ".$port."\n\n";
return $master;
}
function connect($socket){
global $sockets,$users;
$user = new User();
$user->id = uniqid();
$user->socket = $socket;
array_push($users,$user);
array_push($sockets,$socket);
console($socket." CONNECTED!");
}
function disconnect($socket){
global $sockets,$users;
$found=null;
$n=count($users);
for($i=0;$i<$n;$i++){
if($users[$i]->socket==$socket){ $found=$i; break; }
}
if(!is_null($found)){ array_splice($users,$found,1); }
$index = array_search($socket,$sockets);
socket_close($socket);
console($socket." DISCONNECTED!");
if($index>=0){ array_splice($sockets,$index,1); }
}
function dohandshake($user,$buffer){
console("\nRequesting handshake...");
console($buffer);
//list($resource,$host,$origin,$strkey1,$strkey2,$data)
list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
console("Handshaking...");
$acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";
socket_write($user->socket,$upgrade,strlen($upgrade));
$user->handshake=true;
console($upgrade);
console("Done handshaking...");
return true;
}
function getheaders($req){
$r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
}
function getuserbysocket($socket){
global $users;
$found=null;
foreach($users as $user){
if($user->socket==$socket){ $found=$user; break; }
}
return $found;
}
function say($msg=""){ echo $msg."\n"; }
function wrap($msg=""){ return chr(0).$msg.chr(255); }
function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
class User{
var $id;
var $socket;
var $handshake;
}
?>
and the client:
var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
If there is anything wrong in my code can you help me fix it? Console in Firefox says:
Firefox can't establish a connection to the server at ws://localhost:12345/.
I was in the same boat as you recently, and here is what I did:
I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)
I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.
I read the actual WebSocket draft. I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.
I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.
I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: (original link broken) Archived copy.
Please note that the code available at this link has a number of problems and won't work properly without further modification.
I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?
This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.
I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved: What is this data at the end of WebRTC candidate info?
At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.
That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great.
Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script.
Good luck!
Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.
As far as I'm aware Ratchet is the best PHP WebSocket solution available at the moment. And since it's open source you can see how the author has built this WebSocket solution using PHP.
I've searched the minimal solution possible to do PHP + WebSockets during hours, until I found this article:
Super simple PHP WebSocket example
It doesn't require any third-party library.
Here is how to do it: create a index.html containing this:
<html>
<body>
<div id="root"></div>
<script>
var host = 'ws://<<<IP_OF_YOUR_SERVER>>>:12345/websockets.php';
var socket = new WebSocket(host);
socket.onmessage = function(e) {
document.getElementById('root').innerHTML = e.data;
};
</script>
</body>
</html>
and open it in the browser, just after you have launched php websockets.php in the command-line (yes, it will be an event loop, constantly running PHP script), with this websockets.php file.
I was in your shoes for a while and finally ended up using node.js, because it can do hybrid solutions like having web and socket server in one. So php backend can submit requests thru http to node web server and then broadcast it with websocket. Very efficiant way to go.
Need to convert the the key from hex to dec before base64_encoding and then send it for handshake.
$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);
$rawToken = "";
for ($i = 0; $i < 20; $i++) {
$rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
}
$handshakeToken = base64_encode($rawToken) . "\r\n";
$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";
After years of searching for answers in other people's posts, I finally have to ask.
I'm currently trying to implement a WatchDog / Shepherd pattern to monitor long processes (from 2-3 minutes to several hours) running in my app.
The app is fairly complicated, and I started working on it mid-project, so I don't grasp it entirely.
Two servers are running : The one we'll call FACADE, with an Apache/2.4.10 (Debian), and CALCULUS, with an Apache/2.4.6 (Red Hat Enterprise Linux).
I have designed things like this :
The Shepherd initializes on FACADE, when the user click on a button that triggers a long process
The process, running on CALCULUS, initialises a WatchDog that connects to the Shepherd using a TCP Socket.
At some key points of the process, the WatchDog 'barks', i.e sends a string to the Shepherd to tell him at which step the Process is (the string is like "M(essage)#S(tarting)#Indexing", "M#C(ompleted)#Indexing", "M#S#Transfert", "M#C#Transfert"...) and if there's been an error (in which case it sends "E#Indexing" => Error happened during Indexing)
When he gets a Message from the dog, the Shepherd does what he has to (process, displaying to the user, filling a bit the progress bar...)
That my friends, was purely theory. Now comes the Implementation :
/**
*Implements the Singleton Design Pattern
*And of course, the Watchdog Pattern, using a Socket to communicate with the Shepherd.
*/
class Watchdog{
/** Singleton Design pattern. Use $watchdog = Watchdog::getInstance() to use the doggy */
private static $instance = null;
private function __construct(){
$this->init();
}
public static function getInstance(){
if (Watchdog::$instance === null)
Watchdog::$instance = new Watchdog();
return Watchdog::$instance;
}
private $socket;
private $connected = false;
private function init(){
$this->socket = fsockopen("tcp://A.B.C.D", 4242, $errno, $errstr);
if($this->socket === false){
echo "$errno : $errstr";
}else{
$this->connected = true;
}
}
public function kill(){
fclose($this->socket);
Watchdog::$instance = null;
}
public function bark($message){
if($this->connected === true){
fwrite($this->socket, "M#".$message."\n");
}
}
public function alert($err){
if($this->connected === true){
fwrite($this->socket, "E#".$err."\n");
}
}
}
?>
And the Shepeherd :
/**Implements the Singleton Design Pattern,
* And the Shepherd / Watchdog Patter, using a Socket to communicate with the Watchdog.
*/
class Shepherd{
/** Singleton Design pattern. Use $shepherd = Shepherd::GetInstance() to use the shepherd */
private static $instance;
private function __construct()
{
$this->init();
}
public static function GetInstance(){
if (self::$instance === null)
self::$instance = new Shepherd();
return self::$instance;
}
/**Instance variables **/
private $socket;
public $initialised = false;
public $doggyConnected = false;
private $dogsocket;
/**Init : Initializes the shephrd by binding it to the watchdog on the 4242 port**/
private function init(){
ob_implicit_flush();
$this->socket = stream_socket_server("tcp://0.0.0.0:4242", $errno, $errstr);
$this->initialised = true;
}
/**Sit And Wait : Waits for Dog connection**/
public function sitandwait(){
//Waiting for doggy connection
do {
if (($this->dogsocket = stream_socket_accept($this->socket)) === false) {
echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($this->dogsocket)) . "\n";
break;
}else{
$this->doggyConnected = true;
}
}while($this->doggyConnected === false);
}
/**Listen to Dog : Waits for Dog to send a message and echoes it **/
public function listentodog(){
if($this->dogsocket !== false) {
$buf = fgets($this->dogsocket);
return $buf;
}
}
/**Kill : Kills the shepherd and closes connections **/
private function kill(){
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
stream_socket_shutdown($this->dogsocket, STREAM_SHUT_RDWR);
fclose ($this->dogsocket);
fclose ($this->socket);
Shepherd::$instance = null;
}
/**Run : Runs the Shepherd till he got a message from dog **/
public function run(){
if($this->doggyConnected === false)
$this->sitandwait();
return $this->listentodog();
}
}
After trying those without success on the real website, I decided to try and see what was happening on both servers, thanks to netcat command. There's the catch : When I fake the Shepherd thanks to netcat, the dog can connect and send accurate data about the process. When I fake the dog thanks to netcat --send-only, the Shepherd gets the data and does the right things with them.
But when I run both from the application, I get a "Connexion refused" at the dog->init() (fsockopen : Connexion refusée), and of course, the Shepherd dies from Timeout.
But wait there's more !! You might think that the problem comes from the connection, and with netcat It doesn't come up because I don't do the connection from PHP (or I don't connect to a PHP opened socket).
I thought that too; I wrote two scripts, test_dog.php and test_shepherd.php, that are used EXACTLY in the same way than in my real live application. When I try to make them communicate, It works ! It even works with a real dog(monitoring a real application process) and test_shepherd.php, or with a real Shepherd (from my app), and test_watchdog.php
I decided to come here to ask you guys from help, because I'm utterly lost. i don't understand why It doesn't work with the real code, but does with the test_ scripts. In those, I made sure to use the objects exactly the same way than in the real application.
To show you everything, here are my test_ scripts :
test_watchdog.php
require "Watchdog.php";
$dog = Watchdog::getInstance();
$dog->bark("Try try try");
$dog->bark("Trying hard !!");
sleep(5);
$dog->bark("Trying harder to see...");
sleep(2);
$dog->bark("END");
$dog->kill();
test_shepherd.php
require "Shepherd.php";
$shep = new _Shepherd();
echo $shep->run();
... i think that's All. Please answer if you have the faintest idea that might help me, you're my last hope, I'm lost and desperate...
Thank you in advance :)
EDIT : On CALCULUS, the Watchdog is called by a Thousand-lines-long class, called Process (that runs the main process). The point is to be able to call Watchdog nearly everywhere in the code, where the user might have to wait.
Here is for instance the __construct of Process, intializing the Watchdog, and one of the methods that calls the $doggy->bark();
public function __construct($photoId = 0) {
$this->params = array();
$this->doggy = Watchdog::GetInstance();
$this->params['photoid'] = $photoId ;
date_default_timezone_set ('Europe/Paris');
}
public function transfertProject() {
try {
$this->doggy->bark('s#transfert');
//Traitement long
set_time_limit (0);
ini_set('post_max_size', 0);
ini_set('upload_max_filesize', 0);
$response = false;
if (!isset($_FILES['file'])) {
$post_max_size = ini_get('post_max_size');
$upload_max_size = ini_get('upload_max_filesize');
return "Le fichier ne semble pas avoir été posté, vérifier la taille maximal d'upload";
}
$name = $_FILES['file']['name'];
$filename = "../Workspace/projects/".$name;
$tmp = $_FILES['file']['tmp_name'];
if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
$response = $this->unzipProjectArchive();
unlink($filename);
}
$this->doggy->bark('c#transfert');
return $response;
} // END TRY
catch (Exception $ex) {
//Watchdog telling shepherd
$this->doggy->alert('transfert');
}
}
So I'm running PHP on an Azure Web App. Recently, I ran into an issue with my server going into TCP/IP Port Exhaustion, because I was not using persistent connections to my Redis Cache. I don't believe the issue should come up again, but since I don't have any metrics available for this (and since I can't just remote into an Azure Web App), I was wondering if there's any functions in PHP that would allow me to see information about all the TCP ports currently in use by the server, so I can log that for diagnostics.
Does anyone have any ideas on how this can be accomplished? My initial thinking is that I would need to use shell_exec.
Unfortunately, I can't get this to work on Azure Web App, however I think this would work in other scenarios, where you are running your own server.
class netstat {
public $PortsInUse = 0;
public $AvailablePorts = 0;
public $TotalPorts = 0;
public $Ports = array();
public function __construct () {
$p = popen('netsh int ipv4 show dynamicport tcp', 'r');
$this->TotalPorts = rtrim(trim(explode("Number of Ports : ", stream_get_contents($p))[1]));
pclose($p);
$netstat = popen('netstat -no', 'r');
$log = stream_get_contents($netstat);
pclose($netstat);
$this->Ports = array_slice(explode("\n", $log), 4);
array_pop($this->Ports);
foreach($this->Ports as &$port) {
$port = explode(" ", $port);
foreach($port as $k => $p) {
if (empty($p))
unset($port[$k]);
}
$port = array_values($port);
$port = (object)array(
"LocalAddress" => $port[0],
"ForeignAddress" => $port[1],
"Status" => $port[2],
"ProcessId" => $port[3]
);
}
$this->PortsInUse = count($this->Ports);
$this->AvailablePorts = $this->TotalPorts - count($this->Ports);
}
}
The above object will be able to display the current number of TCP ports in use, and the total number of dynamic ports available for TCP on that machine. It also contains a "Ports" array, which contains information about each port in use, such as their local and foreign address, status and PID (process id).
I am trying to create an FTP connection in PHP and trying to access it from other PHP files. Basically my goal is to create an FTP connection and make it available like a session when logging in and then access it from my other PHP files to do other tasks like file upload-download.
My connection class is as follows,
<?php
if($_GET['q'] == 'ftp_connect')
{
$connection = new connect_to_ftp;
echo $connection->ftp_connection();
}
elseif($_GET['q'] == 'get_connection_id')
{
$connection = new connect_to_ftp;
echo $connection->get_connection();
}
class connect_to_ftp
{
public $ftp_server = "";
public $username = "";
public $password = "";
public $connectionID, $login_result, $response;
public function ftp_connection()
{
$this->connectionID = ftp_connect($this->ftp_server);
if($this->connectionID==true)
{
$this->response = array("connection_error" => "Connected to ".$this->ftp_server);
$this->login_result = ftp_login($this->connectionID, $this->username, $this->password);
if($this->login_result==true)
{
$this->response = array("connection_error" => "Login successful to ".$this->ftp_server);
}else
{
$this->response = array("connection_error" => "Failed to login to ".$this->ftp_server." Check username and password");
}
}else
{
$this->response = array("connection_error" => "Could not connect to ".$this->ftp_server);
}
return json_encode($this->response);
}
public function get_connection()
{
return $this->connectionID;
}
}
?>
When I call ftp_connection() using ajax, it successfully connects to the ftp account but later on when I call get_connection() to return me the connection id, it returns me null instead.
Short answer: You can't
Longer answer: file handles, including sockets only exist within the process which opens them (this is not strictly true, it is possible to pass file handles between processes on Linux, but it is very contentious functionality and as with most esoteric things should not be used unless you fully understand the ramifications, and the functionality is not exposed in php).
The options are to either
1) create a connection, carry out the necessary operations then close it within the instance of a php script. Unfortunately FTP is even more badly thought than SMTP but nearly as deeply ingrained. So lots of people have invented built-in security "features" to try to address it's shortcomings. A common one is rate limiting of connections.
2) run an event based daemon to act as the FTP client and connect to that using php. However if you don't understand files and processes then you have a lot of learning to do before you can write such a programming.
Did I mention that FTP sucks? Learn how to use SFTP.