First of all I am sorry for the newbi question ! :D
I want to build a website to do excactly what psands in the following topic did:
http://social.technet.microsoft.com/Forums/en/windowsserver2008r2virtualization/thread/697eafc2-7778-488b-8774-7554f84de642
he built a website to manage virtual machiens such as creat/start/stop.... VMs using the WMI API for Hyper-v with asp.net
now he did it with ASP.NET , my question is can I do it with PHP ?
in another words does the API support PHP ?
Thanks ..
class wmiConnect
{
// WMI connection to specified host
protected $connection;
/**
* Create a new wmi instance.
*
* #param string $host Host name or IP address to connect to
* #param string $username Local host user with rights to query WMI; normally a local admin
* #param string $password Password of local user account
* #return void New wmi object
*/
public function __construct($host = null, $username = null, $password = null) {
$wmiLocator = new \COM('WbemScripting.SWbemLocator');
try {
$this->connection = $wmiLocator->ConnectServer($host, 'root\\CIMV2', $username, $password);
$this->connection->Security_->impersonationLevel = 3;
} catch (\Exception $e) {
// -2147352567 means that we're unable to connect to the local host with a username and password.
// Attempt connection again passing null values for username and password.
if ($e->getCode() == '-2147352567') {
$this->connection = $wmiLocator->ConnectServer($host, 'root\CIMV2', null, null);
$this->connection->Security_->impersonationLevel = 3;
}
}
}
/**
* Get all properties of a WMI class.
*
* #param string $win32_class Win32 class to retrieve data from
* #return object WMI collection object
*/
public function getInfo($win32_class) {
$WMIcollection = $this->connection->ExecQuery('SELECT * FROM ' . $win32_class);
foreach ($WMIcollection as $WMIobj) {
return $WMIobj;
}
}
}
Here is a list of classes for WMI:
https://msdn.microsoft.com/en-us/library/aa394132(v=vs.85).aspx
Hyper-v for 2012 on has a new namespace - https://msdn.microsoft.com/en-us/library/hh850078(v=vs.85).aspx
Related
I plan to write a PHP script that makes an SSH connection. I've investigated how to do this and this looks the most promising solution: https://github.com/phpseclib/phpseclib My only issue is how to handle the fact that my SSH key has a passphrase, and I don't want to have to enter it every time I run the script. For every day SSH use I have ssh-agent running in the background, and it's configured to use pinentry. This makes it so that I don't have to enter my passphrase EVERY time. Any ideas as to how I could get PHP and ssh-agent to talk to each other? My only clue is that ssh-agent sets an environment variable, SSH_AUTH_SOCK, pointing to a socket file.
While the documentation for phpseclib addresses this issue, its answer is silly (just put the passphrase in the code): http://phpseclib.sourceforge.net/ssh/2.0/auth.html#encrsakey
UPDATE: I've looked more into phpseclib and written my own simple wrapper class. However, I cannot get it to login either through ssh-agent or by supplying my RSA key. Only password-based authentication works, contrary to my experiences logging in directly with the ssh command. Here is my code:
<?php
// src/Connection.php
declare(strict_types=1);
namespace MyNamespace\PhpSsh;
use phpseclib\System\SSH\Agent;
use phpseclib\Net\SSH2;
use phpseclib\Crypt\RSA;
use Exception;
class Connection
{
private SSH2 $client;
private string $host;
private int $port;
private string $username;
/**
* #param string $host
* #param int $port
* #param string $username
*
* #return void
*/
public function __construct(string $host, int $port,
string $username)
{
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->client = new SSH2($host, $port);
}
/**
* #return bool
*/
public function connectUsingAgent(): bool
{
$agent = new Agent();
$agent->startSSHForwarding($this->client);
return $this->client->login($this->username, $agent);
}
/**
* #param string $key_path
* #param string $passphrase
*
* #return bool
* #throws Exception
*/
public function connectUsingKey(string $key_path, string $passphrase = ''): bool
{
if (!file_exists($key_path)) {
throw new Exception(sprintf('Key file does not exist: %1$s', $key_path));
}
if (is_dir($key_path)) {
throw new Exception(sprintf('Key path is a directory: %1$s', $key_path));
}
if (!is_readable($key_path)) {
throw new Exception(sprintf('Key file is not readable: %1$s', $key_path));
}
$key = new RSA();
if ($passphrase) {
$key->setPassword($passphrase);
}
$key->loadKey(file_get_contents($key_path));
return $this->client->login($this->username, $key);
}
/**
* #param string $password
*
* #return bool
*/
public function connectUsingPassword(string $password): bool
{
return $this->client->login($this->username, $password);
}
/**
* #return void
*/
public function disconnect(): void
{
$this->client->disconnect();
}
/**
* #param string $command
* #param callable $callback
*
* #return string|false
*/
public function exec(string $command, callable $callback = null)
{
return $this->client->exec($command, $callback);
}
/**
* #return string[]
*/
public function getErrors(): array {
return $this->client->getErrors();
}
}
And:
<?php
// test.php
use MyNamespace\PhpSsh\Connection;
require_once(__DIR__ . '/vendor/autoload.php');
(function() {
$host = '0.0.0.0'; // Fake, obviously
$username = 'user'; // Fake, obviously
$connection = new Connection($host, 22, $username);
$connection_method = 'AGENT'; // or 'KEY', or 'PASSWORD'
switch($connection_method) {
case 'AGENT':
$connected = $connection->connectUsingAgent();
break;
case 'KEY':
$key_path = getenv( 'HOME' ) . '/.ssh/id_rsa.pub';
$passphrase = trim(fgets(STDIN)); // Pass this in on command line via < key_passphrase.txt
$connected = $connection->connectUsingKey($key_path, $passphrase);
break;
case 'PASSWORD':
default:
$password = trim(fgets(STDIN)); // Pass this in on command line via < password.txt
$connected = $connection->connectUsingPassword($password);
break;
}
if (!$connected) {
fwrite(STDERR, "Failed to connect to server!" . PHP_EOL);
$errors = implode(PHP_EOL, $connection->getErrors());
fwrite(STDERR, $errors . PHP_EOL);
exit(1);
}
$command = 'whoami';
$result = $connection->exec($command);
echo sprintf('Output of command "%1$s:"', $command) . PHP_EOL;
echo $result . PHP_EOL;
$command = 'pwd';
$result = $connection->exec($command);
echo sprintf('Output of command "%1$s:"', $command) . PHP_EOL;
echo $result . PHP_EOL;
$connection->disconnect();
})();
The SSH2 class has a getErrors() method, unfortunately it wasn't logging any in my case. I had to debug the class. I found that, whether using ssh-agent or passing in my key, it always reached this spot (https://github.com/phpseclib/phpseclib/blob/2.0.23/phpseclib/Net/SSH2.php#L2624):
<?php
// vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php: line 2624
extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employs multi-factor authentication
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
Obviously the response being returned is of type NET_SSH2_MSG_USERAUTH_FAILURE. There's nothing wrong with the login, of that I'm sure, so per the comment in the code that means the host (Digital Ocean) must use multi-factor authentication. Here's where I'm stumped. What other means of authentication am I lacking? This is where my understanding of SSH fails me.
phpseclib supports SSH Agent. eg.
use phpseclib\Net\SSH2;
use phpseclib\System\SSH\Agent;
$agent = new Agent;
$ssh = new SSH2('localhost');
if (!$ssh->login('username', $agent)) {
throw new \Exception('Login failed');
}
Updating with your latest edits
UPDATE: I've looked more into phpseclib and written my own simple wrapper class. However, I cannot get it to login either through ssh-agent or by supplying my RSA key. Only password-based authentication works, contrary to my experiences logging in directly with the ssh command.
What do your SSH logs look like with both Agent authentication and direct public key authentication? You can get the SSH logs by doing define('NET_SSH2_LOGGING', 2) at the top and then echo $ssh->getLog() after the authentication has failed.
Per neubert, what I had to do was add this line to Connection.php and I was able to get agent-based authentication to work:
$this->client->setPreferredAlgorithms(['hostkey' => ['ssh-rsa']]);
I still can't get key-based authentication to work, but I don't care about that as much.
Trying to understand how to share my session data between my App and my chat server (Ratchet). I thought using Symfony & Memcache would be easy enough but I just can't seem to get it working.
I am trying to get the user_id out of the session for when somebody sends a message to the chat it will insert the user_id into the database (Chat->onMessage).
Can somebody point me in the right direction?
Flow:
config.php is included on every page
When user logs into the website it executes the $login->processLogin() method
I start my chat server via command line (php server.php)
config.php
<?php
use MyApp\Login;
use MyApp\Session as MySession;
# Define backslash or forward slash for *NIX and IIS systems.
define('DS', DIRECTORY_SEPARATOR);
# Attempt to determine the full-server path to the 'root' folder in order to reduce the possibility of path problems.
define('BASE_PATH', realpath(dirname(__FILE__)).DS);
# Define the complete path to the root of the domain we are at (ie. /home/user/domain.com) (does't end in a slash)
define('ROOT_PATH', $_SERVER['DOCUMENT_ROOT']);
# Define where cookies may be active. ('/' means the entire domain)
define('COOKIE_PATH', '/');
# Name sessions. (needs to be alphanumeric with no periods[.]- can't be solely digits; must contain at least one letter)
define('SESSIONS_NAME', 'SiteUser');
# Get the Session Class.
require_once BASE_PATH.'modules'.DS.'Session'.DS.'Session.php';
# Check if there is a session id set the the $sesh_id variable.
$sesh_id=((isset($sesh_id)) ? $sesh_id : NULL);
# Create a new session object, thus starting a new session.
$mysession=MySession::getInstance(NULL, NULL, NULL, $sesh_id);
Login
<?php
namespace MyApp;
use Exception;
# Make sure the script is not accessed directly.
if(!defined('BASE_PATH'))
{
exit('No direct script access allowed');
}
# Get the User Class
require_once BASE_PATH.'modules'.DS.'Login'.DS.'User.php';
/**
* Class Login
*
* The Login Class is used to login in and out users as well as checking various login privileges.
*/
class Login extends User
{
/**
* processLogin
*
* Checks if the Login has been submitted and processes it.
*
* #access public
*/
public function processLogin()
{
if($this->isLoggedIn()===TRUE)
{
header("location: main.php");
die;
}
# Check if the form has been submitted.
if($_SERVER['REQUEST_METHOD']=='POST')
{
try
{
try
{
$this->setLoginSessions($this->getID(), TRUE);
header("location: main.php");
}
catch(Exception $e)
{
throw $e;
}
}
catch(Exception $e)
{
throw $e;
}
}
}
/**
* Checks if user is logged in or not. Returns TRUE if logged in, FALSE if not.
*
* #return bool
*/
public function isLoggedIn()
{
global $mysession;
$symfony_session=$mysession->symfony_session;
//if(!isset($_SESSION['user_logged_in']))
if(!$symfony_session->has('user_id'))
{
# Check if we have a cookie
if(isset($_COOKIE['cookie_id']))
{
try
{
$this->setID($_COOKIE['cookie_id']);
}
catch(Exception $e)
{
unset($_COOKIE['user_ip']);
unset($_COOKIE['athenticate']);
unset($_COOKIE['cookie_id']);
return FALSE;
}
}
else
{
return FALSE;
}
}
//elseif($_SESSION['user_logged_in']===TRUE)
if($symfony_session->get('user_logged_in')===TRUE)
{
return TRUE;
}
return FALSE;
}
/**
* Sets the login sessions.
*
* #param null $user_id
* #param null $logged_in
* #param bool $secure
* #throws Exception
*/
public function setLoginSessions($user_id=NULL, $logged_in=NULL, $secure=FALSE)
{
global $mysession;
$symfony_session=$mysession->symfony_session;
# Check if the user is logged in.
if($this->isLoggedIn()===TRUE)
{
if($user_id===NULL)
{
try
{
# Get the User's data.
$this->findUserData();
$user_id=$this->getID();
$logged_in=TRUE;
}
catch(Exception $e)
{
throw $e;
}
}
}
$symfony_session->set('user_id', $user_id);
$symfony_session->set('user_logged_in', $logged_in);
/*
# Set the User's login sessions.
$_SESSION['user_id']=$user_id;
$_SESSION['user_logged_in']=$logged_in;
*/
}
}
Session
<?php
namespace MyApp;
use Memcache;
use Symfony\Component\HttpFoundation\Session\Session as SymfonySession;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
/**
* Class Session
*
* The Session class is used to access and manipulate Sessions and data stored in them.
*/
class Session
{
private static $session;
private $message=FALSE;
private $sessname=FALSE;
public $symfony_session;
/**
* Session constructor.
*
* Safely calls session_start().
* Also enables sessions to span sub domains. It names the session (which is necessary for
* session_set_cookie_params() to work). If calling this class before setting.php, $sessname (the session name) AND
* $cookiepath (the path for cookies) MUST be defined.
*
* #param null $sessname
* #param null $cookiepath
* #param bool $secure
* #param null $sesh_id
*/
public function __construct($sessname=NULL, $cookiepath=NULL, $secure=FALSE, $sesh_id=NULL)
{
require_once BASE_PATH.'vendor'.DS.'autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$storage=new NativeSessionStorage(array(), new Handler\MemcacheSessionHandler($memcache));
$symfony_session=new SymfonySession($storage);
# Check if a session ID was passed.
if($sesh_id!==NULL)
{
//session_id($sesh_id);
$symfony_session->setId($sesh_id);
}
# Is a session already started?
//if(!isset($_SESSION['s_set']))
if(!$symfony_session->has('s_set'))
{
# If we haven't been given a session name, we will give it one.
if(empty($cookiepath))
{
# Set the default cookie path be the root of the site.
$cookiepath=DS;
# Check if the cookie path was defined in settings.php.
if(defined('COOKIE_PATH'))
{
# Check if the defined path is blank.
if(COOKIE_PATH!='')
{
# If the cookie path has been defined in settings.php, we'll use that path.
$cookiepath=COOKIE_PATH;
}
}
}
//session_set_cookie_params($life, $cookiepath, '.'.DOMAIN_NAME, $secure);
/*
* Read the current save path for the session files and append our own directory to this path.
* Note: In order to make that platform independent, we need to check for the file-seperator first.
* Now we check if the directory already has been created, if not, create one.
* Then we set the new path for the session files.
*/
# Get the session save path.
$save_path=session_save_path();
# Find out if our custom_session folder exists. If not, let's make it.
if(!is_dir(BASE_PATH.'../custom_sessions'.DS.'.'))
{
mkdir(BASE_PATH.'../custom_sessions', 0755);
}
# Is our custom_sessions folder the session save path? If not, let's make it so.
if($save_path!==BASE_PATH.'../custom_sessions')
{
//session_save_path(BASE_PATH.'../custom_sessions');
# How do I set the save path in Symfony?
}
# If we haven't been given a session name, we will give it one.
if(empty($sessname))
{
# Set the default session name.
$sessname='PHPSESSID';
# Check if the session name was defined in settings.php.
if(defined('SESSIONS_NAME'))
{
# Check if the defined name is blank.
if(SESSIONS_NAME!='')
{
# If the session name has been defined in settings.php, we'll give the session that name.
$sessname=SESSIONS_NAME;
}
}
}
$storage->setOptions(array(
'cookie_domain'=>'.'.DOMAIN_NAME,
'cookie_lifetime'=>0,
'cookie_path'=>$cookiepath,
'cookie_secure'=>$secure,
'name'=>$sessname
));
//$this->setSessname($sessname);
# Name the session.
//session_name($this->getSessname());
# Session must be started before anything.
//session_start();
//$session->setName($this->getSessname());
$symfony_session->start();
# Set the s_set session so we can tell if session_start has been called already.
//$_SESSION['s_set']=1;
$symfony_session->set('s_set', 1);
}
$this->symfony_session=$symfony_session;
print_r($symfony_session);exit;
}
/**
* getSessname
*
* Returns the data member $sessname.
*
* #access public
*/
public function getSessname()
{
return $this->sessname;
}
/**
* Sets the data member $sessname. If an empty value is passed, the data member will
* be set with FALSE. Returns the set data member value.
*
* #param $sessname
* #return bool
*/
public function setSessname($sessname)
{
# Clean it up...
$sessname=trim($sessname);
# Check if the passed value is now empty.
if(empty($sessname))
{
# Explicitly set the data member to false.
$sessname=FALSE;
}
# Set the data member.
$this->sessname=$sessname;
# Return the data member after it has gone through the get method.
return $this->getSessname();
}
/**
* Gets the singleton instance of this class.
*
* #param null $sessname
* #param null $cookiepath
* #param bool $secure
* #param null $sesh_id
* #return Session
*/
public static function getInstance($sessname=NULL, $cookiepath=NULL, $secure=FALSE, $sesh_id=NULL)
{
if(!self::$session)
{
self::$session=new Session($sessname, $cookiepath, $secure, $sesh_id);
}
return self::$session;
}
}
server.php
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use Ratchet\Session\SessionProvider;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use MyApp\Chat;
$port="8080";
# Change to this directory.
chdir(dirname(__FILE__));
# Need this for the database insert.
if(!defined('DOMAIN_NAME'))
{
define('DOMAIN_NAME', 'example.dev');
}
require_once '../../includes/lonconfig.php';
require_once '../../vendor/autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$session=new SessionProvider(
new Chat,
new Handler\MemcacheSessionHandler($memcache)
);
$server=IoServer::factory(
new HttpServer(
new WsServer($session)
),
$port,
DOMAIN_NAME
);
$server->run();
Chat
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface
{
protected $clients=array();
public function onOpen(ConnectionInterface $conn)
{
# get the cookies
$cookies=(string)$conn->WebSocket->request->getHeader('Cookie');
# Returns only PHPSESSID (not SiteUser).
//var_dump($cookies);exit;
$this->clients[$conn->resourceId]=$conn;
echo "New connection! ({$conn->resourceId})\n";
}
/**
* #param ConnectionInterface $conn
* #param string $data
*/
public function onMessage(ConnectionInterface $conn, $data)
{
$database=$this->dbh;
$data=json_decode($data, TRUE);
if(isset($data['data']) && count($data['data'])!=0)
{
require_once BASE_PATH.'vendor'.DS.'autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$storage=new NativeSessionStorage(array(), new Handler\MemcacheSessionHandler($memcache));
$session=new SymfonySession($storage);
$type=$data['type'];
$user_id=$conn->Session->get('user_id');
$return=NULL;
if($type=="send" && isset($data['data']['type']) && $user_name!=-1)
{
$msg=htmlspecialchars($data['data']['msg']);
$date=new DateTime;
$date->setTimezone(new DateTimeZone(TIMEZONE));
if($data['data']['type']=='text')
{
echo 'test 4';exit;
$database->query('SELECT `id`, `user_id`, `msg`, `type` FROM `chat` ORDER BY `id` DESC LIMIT 1', array());
$lastMsg=$database->statement->fetch(PDO::FETCH_OBJ);
if($lastMsg->user_id==$user_id && (strlen($lastMsg->msg)<=100 || strlen($lastMsg->msg)+strlen($msg)<=100))
{
# Append message.
$msg=$lastMsg->msg."<br/>".$msg;
$database->query('UPDATE `chat` SET `msg`=:msg, `posted`=NOW() WHERE `id`=:lastmsg', array(
':msg'=>$msg,
':lastmsg'=>$lastMsg->id
));
$return=array(
"id"=>$lastMsg->id,
"name"=>$user_name['staffname'],
"type"=>"text",
"msg"=>$msg,
"posted"=>$date->format("Y-m-d H:i:sP"),
"append"=>TRUE
);
}
else
{
$database->query('INSERT INTO `chat` (`user_id`, `msg`, `type`, `posted`) VALUES (?, ?, "text", NOW())', array(
$user_id,
$msg
));
# Get last insert ID.
$get_chat_id=$database->lastInsertId();
$return=array(
"id"=>$get_chat_id,
"name"=>$user_name['staffname'],
"type"=>"text",
"msg"=>$msg,
"posted"=>$date->format("Y-m-d H:i:sP")
);
}
}
foreach($this->clients as $client)
{
$this->send($client, "single", $return);
}
}
elseif($type=="fetch")
{
# Fetch previous messages.
$this->fetchMessages($conn, $data['data']['id']);
}
}
}
}
You shouldn't actually share that data over all parts of your application.
If you open the tutorial of Ratchet, you will find a part about pushServers.
This allows you to push to a certain channel or $user_id(in your case)
This makes you able to not save or transfer the data in the two parts and will facilitate you to have a streamlined workflow.
I personally use the pushServer in multiple channels, they are split up into:
All users online (sendBroadcast)
All users in a group (sendGroup)
All users following a tag (sendTag)
To other users(sendToUser)
I hope this already gives you an idea on how to solve your problem, otherwise feel free to ask.
I hope i will explain my problem clearly because i feel lost and don't know what is real problem.
Using Symfony 2.8 and https://github.com/iJanki/FtpBundle i'm trying to connect to ftp server and download files. Whole proccess will be launched only through symfony's app/console.
Here's my service which contain whole logic:
<?php
namespace xxx\Services\Import;
use Doctrine\ORM\EntityManager;
use Ijanki\Bundle\FtpBundle\Ftp;
use Ijanki\Bundle\FtpBundle\Exception\FtpException;
use xxx\Entity\Product;
use xxx\Entity\ProductImage;
class ImageImport
{
const FTP_HOST = 'xxx';
const FTP_USER = 'xxx';
const FTP_PASSWORD = 'xxx';
const FTP_PNG_DIR = 'xxx';
const FTP_FILES_EXT = 'png';
/**
*
* #var EntityManager
*/
private $em;
/**
*
* #var string
*/
private $rootDir;
/**
*
* #var Ftp
*/
private $ftp;
/**
*
* #param EntityManager $em
* #param string $rootDir
* #param Ftp $ftp
*/
public function __construct(EntityManager $em, $rootDir, Ftp $ftp)
{
$this->em = $em;
$this->rootDir = $rootDir;
$this->ftp = $ftp;
}
public function import()
{
set_time_limit(10000);
ini_set('max_input_time', 10000);
$this->connectToFtp();
$this->copyAndMatch();
$this->disconnectFtp();
}
/**
*
* #return boolean
*/
public function connectToFtp()
{
try {
$this->ftp->connect(self::FTP_HOST, 21, 10000);
$this->ftp->login(self::FTP_USER, self::FTP_PASSWORD);
$this->ftp->chdir(self::FTP_PNG_DIR);
$this->ftp->pasv(false);
return true;
} catch (FtpException $e) {
echo 'Error: ', $e->getMessage();
return false;
}
}
public function copyAndMatch()
{
$matches = $this->getCsvMatches();
$this->matchWithProducts($matches);
}
public function disconnectFtp()
{
$this->ftp->close();
}
public function __destruct()
{
try {
if (false !== $this->ftp->pwd()) {
$this->ftp->close();
}
} catch (FtpException $ex) {
}
}
/**
*
* #return array|null
*/
private function getCsvMatches()
{
//...
}
/**
*
* #param array $matches
* #return true|null
*/
private function matchWithProducts(array $matches)
{
if (empty($matches) || !is_array($matches)) {
return;
}
foreach ($matches as $pair) {
//...
$filename = $pair['image'] . '.' . self::FTP_FILES_EXT;
if (false === $this->fileAlreadyAdded($product, $filename) and null !== $this->downloadFile($filename)) {
//...
}
}
}
$this->em->flush();
return true;
}
/**
*
* #param Product $product
* #param string $filename
* #return boolean
*/
private function fileAlreadyAdded(Product $product, $filename)
{
//...
}
/**
*
* #param string $filename
* #return string|null
*/
private function downloadFile($filename)
{
try {
$localFile = $this->rootDir . '/../' . ImportHelper::UPLOAD_DIR . '/' . $filename;
if ($this->remoteFileExists($filename)) {
if (false === $this->ftp->pwd()) {
$this->connectToFtp();
}
$this->ftp->get($localFile, $filename, FTP_BINARY);
}
return file_exists($localFile) ? $filename : null;
} catch (FtpException $ex) {
echo $ex->getMessage();
return null;
}
}
/**
*
* #param string $filename
* #return boolean
*/
private function remoteFileExists($filename)
{
return $this->ftp->size($filename) !== -1;
}
}
Commented out or removed code includes Doctrine operations and are not relevant as they don't cause any problems.
As local enviroment I use Xampp with Apache2 server on Windows 7. Also i test FTP connection with Filezilla and i discovered that it works only when using ftp active mode.
Also I discovered that my Windows firewall was reason for warnings and I could connect but do nothing more when it was enabled. So I added exceptions for php.exe and apache httpd.exe so everything works perfectly when php script is launched on my local server.
With Windows firewall enabled, ftp_nlist returned false when using passive mode, and returned warnings when using active mode.
But my goal is to launch that script on production web server which is on VPS which has Ubuntu 14.04.3 LTS installed. My knowledge about Unix is very poor but I was assured by VPS provider that there's no firewall enabled on this server but i get all the time warnings:
PHP Warning: ftp_get(): Could not open data connection to port 39997: Connection timed out
Same warnings that i got when my windows firewall was enabled.
I also called person who's administrating server with FTP (with files i want to dl) and asked him to add exceptions in firewall for ftp connection on port 21 but it didn't help at all.
I'm sure i don't get dicsonnected from ftp server, i never get FtpException. As I can see, download proccess starts because file appears in destination folder but it's empty.
And my problem is that i don't know which side of connection is causing this issues. And what can i do to fix my code to work properly.
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere multiport dports 7727,http
ACCEPT icmp -- anywhere anywhere icmp echo-request
ACCEPT all -- anywhere anywhere
that is output from sudo iptables -L
output from sudo ufw status was Status: inactive
You know what fixed my problem? sudo ufw enable
I still dont know why it's a fix and what was the problem. But it works now.
"In active mode FTP the client connects from a random unprivileged port (N > 1023) to the FTP server's command port, port 21. Then, the client starts listening to port N+1 and sends the FTP command PORT N+1 to the FTP server. The server will then connect back to the client's specified data port from its local data port, which is port 20."
Looks like you have to add this 39997 port to firewall exceptions.
sudo ufw allow out 39997/tcp
sudo ufw allow in 39997/tcp
sudo ufw reload
I'm not sure if this bundle could start to use some other than "39997" port. But looks like it is constant in this case.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a GPS Tracker that connects and send data to a defined public server:port through GPRS connection.
I can define the ip:port of the GPS device
My question is, can I just open a port in my server and listen/save the data received using PHP?
Thanks.
Edit/Update Aug. 16, 2017 :
User and library author <#Navarr> has commented that he has released a new, updated version of the library the code in my original answer was based from. A link to the new code on his github here. Feel free to explore the new code and refer back to the original example here for insight (I have no personally explored/used the new code).
The below code will use the SocketServer.class.php file found here. It is meant to be run as a standalone process which means under Linux I had to make the file executable, then run it from command line using "php my_server.php".
For more information on running php scripts from command line:
http://www.funphp.com/?p=33
First grab the SocketServer.class.php file here:
http://www.phpclasses.org/browse/file/31975.html
Try this to make use of it, then tweak it to handle receiving your own incoming data as needed. Hope it helps.
<?php
require_once("SocketServer.class.php"); // Include the File
$server = new SocketServer("192.168.1.6",31337); // Create a Server binding to the given ip address and listen to port 31337 for connections
$server->max_clients = 10; // Allow no more than 10 people to connect at a time
$server->hook("CONNECT","handle_connect"); // Run handle_connect every time someone connects
$server->hook("INPUT","handle_input"); // Run handle_input whenever text is sent to the server
$server->infinite_loop(); // Run Server Code Until Process is terminated.
function handle_connect(&$server,&$client,$input)
{
SocketServer::socket_write_smart($client->socket,"String? ","");
}
function handle_input(&$server,&$client,$input)
{
// You probably want to sanitize your inputs here
$trim = trim($input); // Trim the input, Remove Line Endings and Extra Whitespace.
if(strtolower($trim) == "quit") // User Wants to quit the server
{
SocketServer::socket_write_smart($client->socket,"Oh... Goodbye..."); // Give the user a sad goodbye message, meany!
$server->disconnect($client->server_clients_index); // Disconnect this client.
return; // Ends the function
}
$output = strrev($trim); // Reverse the String
SocketServer::socket_write_smart($client->socket,$output); // Send the Client back the String
SocketServer::socket_write_smart($client->socket,"String? ",""); // Request Another String
}
Edit: In keeping things relevant and functional for this answer I found it best not to continue to rely on code from an external source that may not always remain available (or at the given URL provided in my link). Therefore, for convenience, I am adding below the code that corresponds to the SocketServer.class.php file I linked to at the top of this post. (Apologies for length and possible lack of indentation/formatting while copy/pasting, I am not the author of the code below).
<?php
/*! #class SocketServer
#author Navarr Barnier
#abstract A Framework for creating a multi-client server using the PHP language.
*/
class SocketServer
{
/*! #var config
#abstract Array - an array of configuration information used by the server.
*/
protected $config;
/*! #var hooks
#abstract Array - a dictionary of hooks and the callbacks attached to them.
*/
protected $hooks;
/*! #var master_socket
#abstract resource - The master socket used by the server.
*/
protected $master_socket;
/*! #var max_clients
#abstract unsigned int - The maximum number of clients allowed to connect.
*/
public $max_clients = 10;
/*! #var max_read
#abstract unsigned int - The maximum number of bytes to read from a socket at a single time.
*/
public $max_read = 1024;
/*! #var clients
#abstract Array - an array of connected clients.
*/
public $clients;
/*! #function __construct
#abstract Creates the socket and starts listening to it.
#param string - IP Address to bind to, NULL for default.
#param int - Port to bind to
#result void
*/
public function __construct($bind_ip,$port)
{
set_time_limit(0);
$this->hooks = array();
$this->config["ip"] = $bind_ip;
$this->config["port"] = $port;
$this->master_socket = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($this->master_socket,$this->config["ip"],$this->config["port"]) or die("Issue Binding");
socket_getsockname($this->master_socket,$bind_ip,$port);
socket_listen($this->master_socket);
SocketServer::debug("Listenting for connections on {$bind_ip}:{$port}");
}
/*! #function hook
#abstract Adds a function to be called whenever a certain action happens. Can be extended in your implementation.
#param string - Command
#param callback- Function to Call.
#see unhook
#see trigger_hooks
#result void
*/
public function hook($command,$function)
{
$command = strtoupper($command);
if(!isset($this->hooks[$command])) { $this->hooks[$command] = array(); }
$k = array_search($function,$this->hooks[$command]);
if($k === FALSE)
{
$this->hooks[$command][] = $function;
}
}
/*! #function unhook
#abstract Deletes a function from the call list for a certain action. Can be extended in your implementation.
#param string - Command
#param callback- Function to Delete from Call List
#see hook
#see trigger_hooks
#result void
*/
public function unhook($command = NULL,$function)
{
$command = strtoupper($command);
if($command !== NULL)
{
$k = array_search($function,$this->hooks[$command]);
if($k !== FALSE)
{
unset($this->hooks[$command][$k]);
}
} else {
$k = array_search($this->user_funcs,$function);
if($k !== FALSE)
{
unset($this->user_funcs[$k]);
}
}
}
/*! #function loop_once
#abstract Runs the class's actions once.
#discussion Should only be used if you want to run additional checks during server operation. Otherwise, use infinite_loop()
#param void
#see infinite_loop
#result bool - True
*/
public function loop_once()
{
// Setup Clients Listen Socket For Reading
$read[0] = $this->master_socket;
for($i = 0; $i < $this->max_clients; $i++)
{
if(isset($this->clients[$i]))
{
$read[$i + 1] = $this->clients[$i]->socket;
}
}
// Set up a blocking call to socket_select
if(socket_select($read,$write = NULL, $except = NULL, $tv_sec = 5) < 1)
{
// SocketServer::debug("Problem blocking socket_select?");
return true;
}
// Handle new Connections
if(in_array($this->master_socket, $read))
{
for($i = 0; $i < $this->max_clients; $i++)
{
if(empty($this->clients[$i]))
{
$temp_sock = $this->master_socket;
$this->clients[$i] = new SocketServerClient($this->master_socket,$i);
$this->trigger_hooks("CONNECT",$this->clients[$i],"");
break;
}
elseif($i == ($this->max_clients-1))
{
SocketServer::debug("Too many clients... :( ");
}
}
}
// Handle Input
for($i = 0; $i < $this->max_clients; $i++) // for each client
{
if(isset($this->clients[$i]))
{
if(in_array($this->clients[$i]->socket, $read))
{
$input = socket_read($this->clients[$i]->socket, $this->max_read);
if($input == null)
{
$this->disconnect($i);
}
else
{
SocketServer::debug("{$i}#{$this->clients[$i]->ip} --> {$input}");
$this->trigger_hooks("INPUT",$this->clients[$i],$input);
}
}
}
}
return true;
}
/*! #function disconnect
#abstract Disconnects a client from the server.
#param int - Index of the client to disconnect.
#param string - Message to send to the hooks
#result void
*/
public function disconnect($client_index,$message = "")
{
$i = $client_index;
SocketServer::debug("Client {$i} from {$this->clients[$i]->ip} Disconnecting");
$this->trigger_hooks("DISCONNECT",$this->clients[$i],$message);
$this->clients[$i]->destroy();
unset($this->clients[$i]);
}
/*! #function trigger_hooks
#abstract Triggers Hooks for a certain command.
#param string - Command who's hooks you want to trigger.
#param object - The client who activated this command.
#param string - The input from the client, or a message to be sent to the hooks.
#result void
*/
public function trigger_hooks($command,&$client,$input)
{
if(isset($this->hooks[$command]))
{
foreach($this->hooks[$command] as $function)
{
SocketServer::debug("Triggering Hook '{$function}' for '{$command}'");
$continue = call_user_func($function,$this,$client,$input);
if($continue === FALSE) { break; }
}
}
}
/*! #function infinite_loop
#abstract Runs the server code until the server is shut down.
#see loop_once
#param void
#result void
*/
public function infinite_loop()
{
$test = true;
do
{
$test = $this->loop_once();
}
while($test);
}
/*! #function debug
#static
#abstract Outputs Text directly.
#discussion Yeah, should probably make a way to turn this off.
#param string - Text to Output
#result void
*/
public static function debug($text)
{
echo("{$text}\r\n");
}
/*! #function socket_write_smart
#static
#abstract Writes data to the socket, including the length of the data, and ends it with a CRLF unless specified.
#discussion It is perfectly valid for socket_write_smart to return zero which means no bytes have been written. Be sure to use the === operator to check for FALSE in case of an error.
#param resource- Socket Instance
#param string - Data to write to the socket.
#param string - Data to end the line with. Specify a "" if you don't want a line end sent.
#result mixed - Returns the number of bytes successfully written to the socket or FALSE on failure. The error code can be retrieved with socket_last_error(). This code may be passed to socket_strerror() to get a textual explanation of the error.
*/
public static function socket_write_smart(&$sock,$string,$crlf = "\r\n")
{
SocketServer::debug("<-- {$string}");
if($crlf) { $string = "{$string}{$crlf}"; }
return socket_write($sock,$string,strlen($string));
}
/*! #function __get
#abstract Magic Method used for allowing the reading of protected variables.
#discussion You never need to use this method, simply calling $server->variable works because of this method's existence.
#param string - Variable to retrieve
#result mixed - Returns the reference to the variable called.
*/
function &__get($name)
{
return $this->{$name};
}
}
/*! #class SocketServerClient
#author Navarr Barnier
#abstract A Client Instance for use with SocketServer
*/
class SocketServerClient
{
/*! #var socket
#abstract resource - The client's socket resource, for sending and receiving data with.
*/
protected $socket;
/*! #var ip
#abstract string - The client's IP address, as seen by the server.
*/
protected $ip;
/*! #var hostname
#abstract string - The client's hostname, as seen by the server.
#discussion This variable is only set after calling lookup_hostname, as hostname lookups can take up a decent amount of time.
#see lookup_hostname
*/
protected $hostname;
/*! #var server_clients_index
#abstract int - The index of this client in the SocketServer's client array.
*/
protected $server_clients_index;
/*! #function __construct
#param resource- The resource of the socket the client is connecting by, generally the master socket.
#param int - The Index in the Server's client array.
#result void
*/
public function __construct(&$socket,$i)
{
$this->server_clients_index = $i;
$this->socket = socket_accept($socket) or die("Failed to Accept");
SocketServer::debug("New Client Connected");
socket_getpeername($this->socket,$ip);
$this->ip = $ip;
}
/*! #function lookup_hostname
#abstract Searches for the user's hostname and stores the result to hostname.
#see hostname
#param void
#result string - The hostname on success or the IP address on failure.
*/
public function lookup_hostname()
{
$this->hostname = gethostbyaddr($this->ip);
return $this->hostname;
}
/*! #function destroy
#abstract Closes the socket. Thats pretty much it.
#param void
#result void
*/
public function destroy()
{
socket_close($this->socket);
}
function &__get($name)
{
return $this->{$name};
}
function __isset($name)
{
return isset($this->{$name});
}
}
Yep, using socket_create, socket_bind, socket_listen, and socket_accept
http://www.php.net/manual/en/function.socket-create.php
http://www.php.net/manual/en/function.socket-bind.php
http://www.php.net/manual/en/function.socket-listen.php
http://www.php.net/manual/en/function.socket-accept.php
There are lots of examples on those pages on how to use them.
Yes, PHP offers a socket_listen() function just for that. http://php.net/manual/en/function.socket-listen.php.
I need to send/return objects or array to/from PHP using SOAP. Any good links?
I am using Zend_Soap_Server и Zend_Soap_Client. I send/receive array of difficult structure.
At first create class with structure you want to receive.
<?php
/**
* Information about people
*/
class PeopleInformation
{
/**
* Name of ...
*
* #var string
*/
public $name;
/**
* Age of
* #var int
*/
public $age;
/**
* Array of family
*
* #var FamilyInformation[]
*/
public $family;
}
/**
* Information about his family
*/
class FamilyInformation
{
/**
* Mother/sister/bro etc
*
* #var string
*/
public $relation;
/**
* Name
* #var string
*/
public $name;
}
?>
Then create service to receive this data:
<?php
/**
* Service to receive SOAP data
*/
class SoapService
{
/**
*
* #param PeopleInformation $data
* #return string
*/
public function getUserData($data)
{
//here $data is object of PeopleInformation class
return "OK";
}
}
?>
Now create Zend_Soap_Server instance in controller by url http://ourhost/soap/:
<?php
//disable wsdl caching
ini_set('soap.wsdl_cache_enabled', 0);
ini_set('soap.wsdl_cache', 0);
$wsdl = $_GET['wsdl'];
//this generate wsdl from our class SoapService
if (!is_null($wsdl))
{
$autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence');
$autodiscover->setClass('SoapService');
$autodiscover->handle();
}
//handle all soap request
else
{
$wsdlPath = 'http://ourhost/soap/?wsdl';
$soap = new Zend_Soap_Server($wsdlPath, array(
'cache_wsdl' => false
));
$soap->registerFaultException('Zend_Soap_Server_Exception');
$soap->setClass('SoapService');
$soap->handle();
}
?>
And now you get wsdl (http://ourhost/soap/?wsdl) with you structure and handle request in SoapService::getUserData. Input parametr in this method is object of PeopleInformation class
Basically you need to create a class map and pass it to your soap client. Yes it is a pain. I usually just have a method that maps the Soap Object name to PHP objects (i.e. Person => MY_Person) and only code the ones I need to by hand (i.e createdOn => DateTime).
class MY_WSHelper
{
protected static $ws_map;
public static function make_map()
{
if( ! self::$ws_map)
{
self::$ws_map = array();
//These will be mapped dynamically
self::$ws_map['Person'] = NULL;
self::$ws_map['Animal'] = NULL;
//Hard-coded type map
self::$ws_map['createdOn'] = DateTime;
self::$ws_map['modifiedOn'] = DateTime;
foreach(self::$ws_map as $soap_name => $php_name)
{
if($php_name === NULL)
{
//Map un-mapped SoapObjects to PHP classes
self::$ws_map[$soap_name] = "MY_" . ucfirst($soap_name);
}
}
}
return self::$ws_map;
}
}
Client:
$client = new SoapClient('http://someurl.com/personservice?wsdl',
array('classmap' => MY_WSHelper::make_map()));
$aperson = $client->getPerson(array('name' => 'Bob'));
echo get_class($aperson); //MY_Person
echo get_class($aperson->createdOn); //DateTime
http://php.net/manual/en/soapclient.soapclient.php
Papa Google points me to this Zend article with lots of good examples on both the client and server aspects of working with Soap (in particular PHP5's implementation of it). Looks like a good starting point.
If you're somewhat like me, and cringe at the thought of writing up a WSDL by hand, I'd recommend using WSHelper, which uses PHP's reflection classes to dynamically generate a WSDL for you. Definitely a time-saver
I replay to share my (bad) experience.
I've created a webservice using PHP ZendFramework2 (ZF2).
The server reply objects and array of objects, and until it taken string as input it worked well. I was using the ArrayOfTypeComplex strategy.
$_strategy = new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex();
When I try to use an array of string as input I felt in a dark and unhappy valley until I found Ramil's answer, so I change strategy and all work right!
$_strategy = new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence();
if (isset($_GET['wsdl'])) {
$autodiscover = new \Zend\Soap\AutoDiscover($_strategy);
$autodiscover->setBindingStyle(array('style' => 'document'));
$autodiscover->setOperationBodyStyle(array('use' => 'literal'));
$autodiscover->setClass('Tracker\Queue\Service')
->setUri($_serverUrl);
echo $autodiscover->toXml();
}