PHP: write_socket() giving unexpected warning - php

I'm trying to set up a WebSocket application, but I'm stuck at the following.
After accepting the socket I received the client's headers as expected, but when I try to send the upgrade back socket_write() throws a warning:
Warning: socket_write(): unable to write to socket [10038]: An operation was attempted on something that is not a socket
This happens in the following piece of code:
var_dump($this->socket); //output: resource(2) of type (Socket)
socket_write($this->socket, $upgrade);
This happens inside a pthreads context.
What are possible reasons PHP is throwing me this warning?
The full code:
public function handshake($headers)
{
Main::console($headers);
Main::console("Getting client WebSocket version...");
Main::console("Headers: \r\n\r\n".$headers);
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
$version = $match[1];
else {
Main::console("The client doesn't support WebSocket");
return false;
}
Main::console("Client WebSocket version is {$version}, (required: 13)");
if($version == 13) {
// Extract header variables
Main::console("Getting headers...");
if(preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if(preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if(preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
Main::console("Client headers are:\r\n\r\n".
"- Root: ".$root."\r\n".
"- Host: ".$host."\r\n".
"- Origin: ".$origin."\r\n".
"- Sec-WebSocket-Key: ".$key."\n");
Main::console("Generating Sec-WebSocket-Accept key...");
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: $acceptKey".
"\r\n\r\n";
Main::console("Sending this response to the client #{$this->getId()}:\r\n\r\n".$upgrade);
var_dump($this->socket);
socket_write($this->socket, $upgrade, strlen($upgrade));
$this->setHandshake(true);
Main::console("Handshake is successfully done!");
return true;
}
else {
Main::console("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
public function run()
{
while($this->alive)
{
$bytes = #socket_recv($this->socket, $buffer, 4096, MSG_WAITALL);
if ($buffer)
{
if(!$this->handshake)
{
$this->handshake($buffer);
} else {
Main::console("Client {$this->getID()} says {$buffer}");
}
}
}
}

Try socket_last_error() and socket_strerror() for more information. If that doesn't help, show us how the socket is created. Source: http://www.php.net/manual/en/function.socket-last-error.php

Related

Socket.io 3 and PHP integration

I using PHP SocketIO class to connect NodeJS application and send messages.
Everything worked wonderfully with Socket.io 2 but after upgrade to version 3 the PHP integration is stopped working.
When I send request I am getting this response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hNcappwZIQEbMz7ZGWS71lNcROc=
But I don't see anything on NodeJS side, even when I tried to log any connection to the server by using "connection" event.
This is the PHP class:
class SocketIO
{
/**
* #param null $host - $host of socket server
* #param null $port - port of socket server
* #param string $action - action to execute in sockt server
* #param null $data - message to socket server
* #param string $address - addres of socket.io on socket server
* #param string $transport - transport type
* #return bool
*/
public function send($host = null, $port = null, $action= "message", $data = null, $address = "/socket.io/?EIO=2", $transport = 'websocket')
{
$fd = fsockopen($host, $port, $errno, $errstr);
if (!$fd) {
return false;
} //Can't connect tot server
$key = $this->generateKey();
$out = "GET $address&transport=$transport HTTP/1.1\r\n";
$out.= "Host: https://$host:$port\r\n";
$out.= "Upgrade: WebSocket\r\n";
$out.= "Connection: Upgrade\r\n";
$out.= "Sec-WebSocket-Key: $key\r\n";
$out.= "Sec-WebSocket-Version: 13\r\n";
$out.= "Origin: https://$host\r\n\r\n";
fwrite($fd, $out);
// 101 switching protocols, see if echoes key
$result= fread($fd,10000);
preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $result, $matches);
$keyAccept = trim($matches[1]);
$expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$handshaked = ($keyAccept === $expectedResonse) ? true : false;
if ($handshaked){
fwrite($fd, $this->hybi10Encode('42["' . $action . '", "' . addslashes($data) . '"]'));
fread($fd,1000000);
return true;
} else {return false;}
}
private function generateKey($length = 16)
{
$c = 0;
$tmp = '';
while ($c++ * 16 < $length) { $tmp .= md5(mt_rand(), true); }
return base64_encode(substr($tmp, 0, $length));
}
private function hybi10Encode($payload, $type = 'text', $masked = true)
{
$frameHead = array();
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
$frameHead[0] = 129;
break;
case 'close':
$frameHead[0] = 136;
break;
case 'ping':
$frameHead[0] = 137;
break;
case 'pong':
$frameHead[0] = 138;
break;
}
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
if ($frameHead[2] > 127) {
$this->close(1004);
return false;
}
} elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
}
Thank you for help!
I was having the same problem with all the libraries that exists on github, the problem is that they are abandoned or not updated to socket.io V3.
In socket.io documentation says:
TL;DR: due to several breaking changes, a v2 client will not be able to connect to a v3 server (and vice versa)
To solve this problem, you need to learn how socket.io client works, this is easy because is in the protocol documentation, in the sample-session section.
Socket.Io protocol documentation
To solve this, you will need to forget the fsockopen and fwrite functions, you need to use CURL directly doing the requests mentioned in the protocol documentation.
Request n°1
GET
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H
Open packet: Open the connection between php and socket.io server. The server will return a "session id" named "sid", you will be adding this to the url query for the subsecuent queries.
Request n°2
POST
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
post body: '40'
Namespace connection request: You need to send in the body the number 40, as a string, this means that you want to connect to socket.io "message" type
Request n°3
GET
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
Namespace connection approval : This will return if the connection is successful or if there is an error, here is when the socket.io server authorizes your connection if you need a token.
Request n°4
POST
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
post body: 42[event,data]
For example 42["notifications","Hi, Im a notification"] and is equivalent to socket.emit(event,data)
Emit message to server: Send your message to the socket.io server.
Here is a BASIC example using Symfony 5.2 and HttpClientInterface:
<?php
// install dependencies before: composer require symfony/http-client
use Symfony\Component\HttpClient\CurlHttpClient;
include('vendor/autoload.php');
$client = new CurlHttpClient();
sendToSocket($client);
function sendToSocket(HttpClientInterface $client)
{
$first = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=N8hyd6w');
$res = ltrim($first->getContent(), '0');
$res = json_decode($res, true);
$sid = $res['sid'];
$second = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '40'
]);
$third = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid);
$fourth = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '42["notifications","Hi, Im a notification"]'
]);
}
As you can see, is very easy, and you dont need the troubling "copy-pasted" libraries out there. I said "copy-pasted" because all use the same code to open de socket and send the information, but no one is compatible with socket.io V3.
Here is an image, proving that the given code works as January 4 2021 with php 7.4, symfony 5.2 and socket.io V3.
This is my test server in node
// Install dependencies before: npm i express socket.io
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.on('connection', function (socket) {
console.log("New Connection with transport", socket.conn.transport.name);
socket.on('notifications', function (data) {
console.log(data);
});
});
http.listen(3000, () => {
console.log('Server started port 3000');
});
I need to say that this solution works excellent if you want to send "one direction" messages to your socket.io server, like a new notification or whatever that doesn't need a permanent connection, is just "one shot" and nothing else.
Happy coding and greetings from Mexico.
Here is another example:
First column is Postman making a request to the php server, simulating a server side event, like a new question created. In the response are the dumps of the response body from the 4 requests that you need to make.
Second column is the socket.IO node server running on port 3000
And the last column is the chrome console, simulating a user connected to the socket.IO server via websocket looking for notifications in 'questions' event.

PHP Warning: fgets(): SSL operation failed with code 1

This is the error log, and i have no idea how to solve this.
[Thu Jan 25 10:39:42.689306 2018] [:error] [pid 21084]
PHP Warning: fgets(): SSL operation failed with code 1.
OpenSSL Error messages:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypterror:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypterror:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypterror:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt in ../vendor/pear-pear.horde.org/Horde_Imap_Client/Horde/Imap/Client/Socket/Connection/Socket.php on line 156
public function read($size = null)
{
$got_data = false;
$literal_len = null;
$token = new Horde_Imap_Client_Tokenize();
do {
if (feof($this->_stream)) {
$this->close();
$this->_params['debug']->info(
'ERROR: Server closed the connection.'
);
throw new Horde_Imap_Client_Exception(
Horde_Imap_Client_Translation::r("Mail server closed the connection unexpectedly."),
Horde_Imap_Client_Exception::DISCONNECT
);
}
if (is_null($literal_len)) {
$buffer = '';
while (($in = fgets($this->_stream)) !== false) {
$got_data = true;
if (substr($in, -1) === "\n") {
$in = rtrim($in);
$this->_params['debug']->server($buffer . $in);
$token->add($in);
break;
}
$buffer .= $in;
$token->add($in);
}
/* Check for literal data. */
if (is_null($len = $token->getLiteralLength())) {
break;
}
// Skip 0-length literal data.
if ($len['length']) {
$binary = $len['binary'];
$literal_len = $len['length'];
}
continue;
}
$old_len = $literal_len;
while (($literal_len > 0) && !feof($this->_stream)) {
$in = fread($this->_stream, min($literal_len, 8192));
/* Only store in stream if this is something more than a
* nominal number of bytes. */
if ($old_len > 256) {
$token->addLiteralStream($in);
} else {
$token->add($in);
}
if (!empty($this->_params['debugliteral'])) {
$this->_params['debug']->raw($in);
}
$got_data = true;
$literal_len -= strlen($in);
}
$literal_len = null;
if (empty($this->_params['debugliteral'])) {
$this->_params['debug']->server('[' . ($binary ? 'BINARY' : 'LITERAL') . ' DATA: ' . $old_len . ' bytes]');
}
} while (true);
if (!$got_data) {
$this->_params['debug']->info('ERROR: read/timeout error.');
throw new Horde_Imap_Client_Exception(
Horde_Imap_Client_Translation::r("Error when communicating with the mail server."),
Horde_Imap_Client_Exception::SERVER_READERROR
);
}
return $token;
}
The error appears at
while (($in = fgets($this->_stream)) !== false) {
I have another Instance of my application on the same server with the same settings just different database and domain name.
The other Instance seems to work just fine without having problems with this error.
Note: Horde is a kind of PHP library for sending Emails.
Can anyone help me?
Please try to change your setting as below:
<?php
$yourArrayOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
)
$response = file_get_contents("fileName", false,
stream_context_create($yourArrayOptions));
echo $response; ?>
please click here for better explanation file_get_contents(): SSL operation failed with code 1. And more

How to fix Websocket Handshake code?

This is likely a familiar sob story. But there are so many of them out there, and I'm such a n00b I can't find the answer, so I'd like your help if you can help me.
So, I'm using phpwebsocket by lemmingzshadow (google brings this up pretty easily if you are unfamiliar). As far as I can tell the version he has out has a bug where it doesn't follow the standards that Chrome 20.+ now uses. Its got something to do with the hand shake & security keys but that's where I'm stuck at. I know I need to provide the following based on other questions, hopefully you can help me understand and fix this issue:
The header Chrome receives is (Edited; I apparently posted the message to the server twice.):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: aWXrpLOnEm15mE8+w1zG05ad01k=
Sec-WebSocket-Protocol: QuatroDuo
The header my server receives is:
Upgrade: websocket
Connection: Upgrade
Host: gumonshoe.net:8000
Origin: http://gumonshoe.net
Sec-WebSocket-Key: v3+iw0U78qkwZnp+RWTu3A
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
I don't think the cookies are necessary, correct me if I'm wrong though.
I hate to do this next part, but I figure pasting it all is better than doing nothing and needing to come back later. Here is the portion of code that reads & interprets the handshake and sends the new one.
Help is appreaciated:
<?PHP
private function handshake($data)
{
$this->log('Performing handshake\r\n\r\n' . $data);
$lines = preg_split("/\r\n/", $data);
// check for valid http-header:
if(!preg_match('/\AGET (\S+) HTTP\/1.1\z/', $lines[0], $matches)) {
$this->log('Invalid request: ' . $lines[0]);
$this->sendHttpResponse(400);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
return false;
}
// check for valid application:
$path = $matches[1];
$this->application = $this->server->getApplication(substr($path, 1));
if(!$this->application) {
$this->log('Invalid application: ' . $path);
$this->sendHttpResponse(404);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
// generate headers array:
$headers = array();
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
// check for supported websocket version:
if(!isset($headers['Sec-WebSocket-Version']) || $headers['Sec-WebSocket-Version'] < 6)
{
$this->log('Unsupported websocket version.');
$this->sendHttpResponse(501);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
// check origin:
if($this->server->getCheckOrigin() === true)
{
$origin = (isset($headers['Sec-WebSocket-Origin'])) ? $headers['Sec-WebSocket-Origin'] : false;
$origin = (isset($headers['Origin'])) ? $headers['Origin'] : $origin;
if($origin === false)
{
$this->log('No origin provided.');
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
if(empty($origin))
{
$this->log('Empty origin provided.');
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
if($this->server->checkOrigin($origin) === false)
{
$this->log('Invalid origin provided. : ' . $origin . ' Legal options were:');
$gumk = 0;
foreach(array_keys($this->server->getAllowedOrigins()) as $lo) {
$this->log( '[' . $gumk++ . '] : ' . $lo);
}
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
}
// do handyshake: (hybi-10)
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$response = "HTTP/1.1 101 Switching Protocols\r\n";
$response.= "Upgrade: websocket\r\n";
$response.= "Connection: Upgrade\r\n";
$response.= "Sec-WebSocket-Accept: " . $secAccept . "\r\n";
$response.= "Sec-WebSocket-Protocol: " . substr($path, 1) . "\r\n\r\n";
if(false === ($this->server->writeBuffer($this->socket, $response)))
{
return false;
}
$this->handshaked = true;
$this->log('Handshake sent');
$this->application->onConnect($this);
// trigger status application:
if($this->server->getApplication('status') !== false)
{
$this->server->getApplication('status')->clientConnected($this->ip, $this->port);
}
return true;
}
Receiving the following error,
Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch
As I'm largely inexperienced in this level of server debugging, a more detailed answer than linking me to documentation/specifications would be appreciated.
If any of you are beating your head against a wall, this is the offending piece of code:
$response.= "Sec-WebSocket-Protocol: " . substr($path, 1) .
I am sure there is a way to actually set the desired/possible protocols, but I'm not sure yet what they are; and I'm not sure if its necessary for my purposes. If someone has an explanation of what the protocol switching is even for, I'd love to read it, but for now I'm just taking it out of my code.
Lots of googling to find this small problem.
I also dumped the pack(H*) code in the handshake which didn't seem to be necessary based on what I was reading. I'm not sure if that did anything or not, but it wasn't necessary to get the program to work.

WebSocket handshake not working

I'm making a simple WebSocket server in PHP. My websocket client connects to it fine, but any time I try to send data through it I get an "Error: INVALID_STATE_ERR: DOM Exception 11" thrown in JavaScript.
This and a couple other questions seem to describe the same problem I'm having but the WebSocket protocol has changed since.
I'm assuming the problem is that my script is handshaking incorrectly as stated in that question. I'm using Chromium 15 which uses WebSocket version 8.
Here's my handshake function (partially my code partially modified from an outdated example I found somewhere):
function dohandshake($user, $buffer)
{
server_log(1, 'Requesting handshake...');
// Determine which version of the WebSocket protocol the client is using
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/ ", $buffer, $match))
$version = $match[1];
else
return false;
if($version == 8)
{
// Extract header variables
if(preg_match("/GET (.*) HTTP/" ,$buffer,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$buffer,$match)){ $h=$match[1]; }
if(preg_match("/Sec-WebSocket-Origin: (.*)\r\n/",$buffer,$match)){ $o=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$buffer,$match)){ $k = $match[1]; }
// Generate our Socket-Accept key based on the IETF specifications
$accept_key = $k . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$accept_key = sha1($accept_key, true);
$accept_key = base64_encode($accept_key);
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept: $accept_key";
socket_write($user->socket, $upgrade, strlen($upgrade));
$user->handshake = true;
return true;
}
else
{
server_log("Client is trying to use an unsupported WebSocket protocol ({$version})");
return false;
}
}
I tested the key generation code on several examples I found and it seems to have returned the correct key according to those examples
Dumb solution of the century, apparently two "\r\n" newlines are expected at the end of the handshake response.

Using PHP to read a web page with fsockopen(), but fgets is not working

Im using this code here: http://www.digiways.com/articles/php/httpredirects/
public function ReadHttpFile($strUrl, $iHttpRedirectMaxRecursiveCalls = 5)
{
// parsing the url getting web server name/IP, path and port.
$url = parse_url($strUrl);
// setting path to '/' if not present in $strUrl
if (isset($url['path']) === false)
$url['path'] = '/';
// setting port to default HTTP server port 80
if (isset($url['port']) === false)
$url['port'] = 80;
// connecting to the server]
// reseting class data
$this->success = false;
unset($this->strFile);
unset($this->aHeaderLines);
$this->strLocation = $strUrl;
$fp = fsockopen ($url['host'], $url['port'], $errno, $errstr, 30);
// Return if the socket was not open $this->success is set to false.
if (!$fp)
return;
$header = 'GET / HTTP/1.1\r\n';
$header .= 'Host: '.$url['host'].$url['path'];
if (isset($url['query']))
$header .= '?'.$url['query'];
$header .= '\r\n';
$header .= 'Connection: Close\r\n\r\n';
// sending the request to the server
echo "Header is: <br />".str_replace('\n', '\n<br />', $header)."<br />";
$length = strlen($header);
if($length != fwrite($fp, $header, $length))
{
echo 'error writing to header, exiting<br />';
return;
}
// $bHeader is set to true while we receive the HTTP header
// and after the empty line (end of HTTP header) it's set to false.
$bHeader = true;
// continuing untill there's no more text to read from the socket
while (!feof($fp))
{
echo "in loop";
// reading a line of text from the socket
// not more than 8192 symbols.
$good = $strLine = fgets($fp, 128);
if(!$good)
{
echo 'bad';
return;
}
// removing trailing \n and \r characters.
$strLine = ereg_replace('[\r\n]', '', $strLine);
if ($bHeader == false)
$this->strFile .= $strLine.'\n';
else
$this->aHeaderLines[] = trim($strLine);
if (strlen($strLine) == 0)
$bHeader = false;
echo "read: $strLine<br />";
return;
}
echo "<br />after loop<br />";
fclose ($fp);
}
This is all I get:
Header is:
GET / HTTP/1.1\r\n
Host: www.google.com/\r\n
Connection: Close\r\n\r\n
in loopbad
So it fails the fgets($fp, 128);
Is there a reason you aren't using PHP's built-in, enabled-by-default ability to fetch remote files using fopen?
$remote_page = file_get_contents('http://www.google.com/'); // <- Works!
There are also plenty of high-quality third-party libraries, if you need to do something like fetch headers without thinking too hard. Try Zend_Http_Client on for size.
The flaw is here:
$good = $strLine = fgets($fp, 128);
if(!$good)
{
echo 'bad';
return;
}
fgets() returns either a string on success, or FALSE on failure. However, if there was no more data to be returned, fgets() will return the empty string (''). So, both $good and $strLine are set to the empty string, which PHP will happily cast to FALSE in the if() test. You should rewrite as follows:
$strLine = fgets($fp, 128);
if ($strLine === FALSE) { // strict comparison - types and values must match
echo 'bad';
return;
}
There's no need for the double assignment, as you can test $strLine directly.

Categories