I have a set of classes for data storage. It currently have a working connector for Google Drive, Dropbox, Box, FTP and local connection. I have designed this to construct a file manager where a client can manage multiple data storage sources.
Here comes my question. Currently, my implementation of the addConnection() method is the following:
// addConnection - adds a storage connection to the storage manager
public function addConnection($service, $credentials) {
if (!empty($service) && !empty($credentials)) {
if (in_array($service, [Connectors\Box, Connectors\Dropbox, Connectors\FTP, Connectors\GoogleDrive, Connectors\Local], true)) {
// rest goes here...
}
}
}
I have my constants in a separate namespace like this:
namespace Services\Storage\Connectors {
const Box = "Box";
const Dropbox = "Dropbox";
const FTP = "FTP";
const GoogleDrive = "GoogleDrive";
const Local = "Local";
}
Is there a way for me to get all defined constant for a given namespace? That way, I could construct a function to get all defined constants and write something more elegant like in_array($service, getConstants($this->getNamespace()), true).
Can you give me a hint?
You can get all user defined constants with get_defined_constants and then check the namespace using strpos:
$constants = get_defined_constants(TRUE)['user'];
foreach ($constants as $name => $value)
{
if (0 === strpos($name, 'services\storage\connectors'))
{
$services_storage_connectors[] = $name;
}
}
echo '<pre>' . print_r($services_storage_connectors, TRUE) . '</pre>';
/*
Array
(
[0] => services\storage\connectors\Box
[1] => services\storage\connectors\Dropbox
[2] => services\storage\connectors\FTP
[3] => services\storage\connectors\GoogleDrive
[4] => services\storage\connectors\Local
)
*/
Note that $name is lowercase, even if the namespace is defined with uppercase letters.
Edit: In case you'd like it in fewer lines
$constants = array_filter(array_keys(get_defined_constants(TRUE)['user']), function ($name)
{
return 0 === strpos($name, 'services\storage\connectors');
}
);
Related
I have several dictionaries in my application for internationalization. Depending on the version of the app, different languages should be included, so it is not necessary to deliver all available languages.
The setting which languages can be used, I want to set in constants for simplicity.
My dictionaries themselves are simple 'getDictionary()' functions that return an array of translations. To distinguish the different languages, the respective functions are in namespaces, e.g. Language\enGB and Language\esMX.
Dictionary for English (Great Britain).
namespace Language\enGB;
function getDictionary(): array
{
// ...
}
Dictionary for Spanish (Mexico).
namespace Language\enGB;
function getDictionary(): array
{
// ...
}
The parent class that uses these dictionaries is I18n. This class should dynamically include the dictionaries.
namespace Language;
// Include all available languages.
include_once __DIR__ . '/Dictionary/enGB.php';
include_once __DIR__ . '/Dictionary/enUS.php';
include_once __DIR__ . '/Dictionary/esMX.php';
// Supported languages that can be used by the client.
const SUPPORTED_LANGUAGES = ['en-GB', 'en-US'];
// Function definitions for all
const LANGUAGE_IMPORTS = [
'en-GB' => 'enGB\getDictionary',
'en-US' => 'enUS\getDictionary',
'es-MX' => 'esMX\getDictionary'
];
class I18n
{
private array $dictionary = [];
public function __construct(string $isoCode)
{
// Call all dictionary functions from all supported languages.
// Unsupported languages are not included.
foreach (SUPPORTED_LANGUAGES as $supportedLanguage) {
$functionName = LANGUAGE_IMPORTS[$supportedLanguage];
if (!function_exists($functionName)) continue;
$functionName();
}
}
}
Now I have the following problem: I cannot call the functions because they do not exist. The line if (!function_exists($functionName)) continue; always goes to the true case (continue).
I can't quite figure it out since 1) the functions have been included via include_once and 2) the namespace is specified with in the function name. Where is the problem here ?
I have searched through many many topics and none have helped me with my specific need as yet.
I tried many options from This Thread but none have been quite right.
I am making a config generator for switches and routers,
I am trying to streamline it, so rather than have a config array for each interface, i'll have config array for each type of layout and have the interface name as a variable.
I have the web page posting to the script and I have the page posting the port layout per post, with each interface as its own variable.
i.e.
$fa1 = "DESKTOP";
$fa2 = "VOIP";
$fa3 = "DESKTOP";
$fa4 = "";
$fa5 = "DESKTOP";
etc. I am trying to use a foreach loop to go through each interface (as such)
$ints = array($fa1,$fa2,$fa3,$fa4,etc);
foreach ($ints as $line) {
if ($line == "PC") {
file_put_contents($file, $pcarray.FILE_APPEND);
}
if ($line == "VOIP") {
file_put_contents($file,$voiparray.FILE_APPEND);
}
}
with the interface setup as an array:
$pcarray = array(
'interface ' .$interface,
'configetc'
);
I need a function to get the name of the variable for the interface so I can assign it to the $interface variable.
all the examples I've found and tried thus far dont seem compatible with like valued variables.
ie. for the above, most would return fa1 for everything that comes up DESKTOP rather than fa3 for the next desktop one.
an example of the ones that dont quite work how I want is:
function print_var_name($var) {
foreach($GLOBALS as $var_name => $value) {
if ($value === $var) {
return $var_name;
}
}
return false;
}
This one, for each variable value repetition after the first just returned the first variables name.
EDIT:
What I am wanting to get back would be:
(as the written array)
interface fa1
standard desktop config
interface fa2
standard voip config
interface fa3
standard desktop config
interface fa5
standard desktop config
where as currently im getting:
interface fa1
standard desktop config
interface fa2
standard voip config
interface fa1
standard desktop config
interface fa1
standard desktop config
I'm not sure if this is what your after, but in the example below, I've just created a foreach() loop to check if the array value from $ints[] is true. If so, then add variable name and value to $varArr array. So the new array would look like ["fa1" => "DESKTOP", "fa2" => "VOIP"]. You then loop through this new array and compare the value using a Switch. Note that $fa4 has been ignored on purpose but you can easily add this in if needs be and test using a default case.
$fa1 = "DESKTOP";
$fa2 = "VOIP";
$fa3 = "DESKTOP";
$fa4 = "";
$fa5 = "DESKTOP";
$ints = [$fa1,$fa2,$fa3,$fa4,$fa5];
$varArr; //Variable Name Array
foreach($GLOBALS as $k=>$v) { //Looping through Globals
if(in_array($v, $ints) && $v !== "") { //If value is in $ints array, add to this array
$varArr[$k] = $v;
}
}
foreach ($varArr as $k=>$v) { //Create Interface File
file_put_contents($file, "interface ". $k ."\n", FILE_APPEND);
switch($v) {
case "DESKTOP":
file_put_contents($file, "standard desktop config\n", FILE_APPEND);
break;
case "VOIP":
file_put_contents($file, "standard voip config\n", FILE_APPEND);
break;
}
}
Output:
interface fa1
standard desktop config
interface fa2
standard voip config
interface fa3
standard desktop config
interface fa5
standard desktop config
You can't get variable names the way you're trying to. You should instead pass the list of variable names to look at instead if you need to keep the var names. Then your function that generates your content will just use what you have in the array.
Something simple like this would give you both the value of your variable and its name ($varname is in the foreach loop). (I have no idea where $file, $pcarray and $voiparray come from, so they're just globals in the sample function):
$list = ["fa1", "fa2", "fa3", "fa4", "fa5"];
PutFileContentList($list);
function PutFileContentList(array $list) {
globals $file, $pcarray, $voiparray;
foreach ($list as $varname) {
$line = isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : "";
if ($line == "PC") {
file_put_contents($file, $pcarray . FILE_APPEND);
}
if ($line == "VOIP") {
file_put_contents($file, $voiparray . FILE_APPEND);
}
}
}
The following code works and does what I want, but I'm pretty sure I'm doing something dumb\awful.
I'm learning OOP and there is a tutorial I started to follow that used a "Config" class to setup some parameters for the program to use. I've noticed something similar in other tutorials. This tutorial though only included a method to retrieve the configuration (it used the $GLOBALS array) not to update it during the run time of the program. I attempted to add this functionality, but resorted to using eval() which I think is a nono? Also it was never explained in the tutorial why the $GLOBALS array was used instead of just using a static variable so I'm confused about that as well.
Here is init.php which gets included in files needing to access the config options:
<?php
$GLOBALS['config'] = array(
'mysql' => array(
'host' => '127.0.0.1',
'username' => 'root',
'password' => '123456',
'db' => NULL
),
'shell' => array(
'exe' => 'powershell.exe',
'args' => array(
'-NonInteractive',
'-NoProfile',
'-NoLogo',
'-Command'
)
)
);
spl_autoload_register(function($class){
require_once 'classes/' . $class . '.php';
});
This is the Config.php class which has a get and (my) set method to access the config array. For the set method I build a string like "$GLOBALS['config']['someConfig']['someSubConfig'] = 'newVal';" and use eval to execute it. Ultimately I use it in the program like Config::set('mysql/host','zzzzz');
<?php
class Config {
public static function get($path=NULL) {
//return all configs if not specified
$config = $GLOBALS['config'];
if($path) {
//parse path to return config
$path = explode('/', $path);
foreach($path as $element) {
if(isset($config[$element])) {
$config = $config[$element];
} else {
//if config not exist
$config = false;
}
}
}
return $config;
}
public static function set($path=NULL,$value=NULL) {
if($path) {
//parse path to return config
$path = explode('/', $path);
//Start code string for eval
$globalPosition = '$GLOBALS['."'config'".']';
foreach($path as $element) {
$globalPosition .= "['$element']";
}
$globalPosition .= "='$value';";
//End code string
eval($globalPosition);
var_dump($GLOBALS);
}
}
}
First of all, here are a few caveats:
Global variables are rarely a good idea, especially in OOP design (mainly because they couple code very tightly).
Please don't use eval().
You can quite easily modify your code to set the variable (by reference using =&) without having to use eval() at all. For example:
public static function set($path = null,$value = null)
{
if($path)
{
//parse path to return config
$path = explode('/', $path);
//Start code string for eval
$setting =& $GLOBALS['config'];
foreach($path as $element)
{
$setting =& $setting[$element];
}
$setting = $value;
var_dump($GLOBALS);
}
}
Is there any elegant way to check if a file was included by using include/include_once/require/require_once or if the page was actually loaded directly? I'm trying to set up a testing file inside class files while I'm creating them.
I'm looking for something similar to Python's if __name__ == "__main__": technique. Without setting globals or constants.
Quoted from: How to know if php script is called via require_once()?
I was looking for a way to determine if a file have been included or called directly, all from within the file. At some point in my quest I passed through this thread. Checking various other threads on this and other sites and pages from the PHP manual I got enlightened and came up with this piece of code:
if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
echo "called directly";
} else {
echo "included/required";
}
In essence it compares if the name of the current file (the one that
could be included) is the same as the file that is beeing executed.
Credit: #Interwebs Cowboy
you can do this by get_included_files — Returns an array with the names of included or required files and validate against __FILE__
I appreciate all the answers, but I didn't want to use any one's solution here, so I combined your ideas and got this:
<?php
// place this at the top of the file
if (count(get_included_files()) == 1) define ('TEST_SUITE', __FILE__);
// now I can even include bootstrap which will include other
// files with similar setups
require_once '../bootstrap.php'
// code ...
class Bar {
...
}
// code ...
if (defined('TEST_SUITE') && TEST_SUITE == __FILE__) {
// run test suite here
}
?>
if (defined('FLAG_FROM_A_PARENT'))
// Works in all scenarios but I personally dislike this
if (__FILE__ == get_included_files()[0])
// Doesn't work with PHP prepend unless calling [1] instead.
if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
// May break on Windows due to mixed DIRECTORY_SEPARATOR
if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
// Doesn't work with files with the same basename but different paths
if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))
// Seems to do the trick as long as document root is properly configured
Note: On WAMP Servers virtual-hosts sometimes inherit the default document root setting, causing $_SERVER['DOCUMENT_ROOT'] to display wrong path.
<?php
if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
{
//file was navigated to directly
}
?>
Taken from mgutt's answer to a slightly different question here. It's important to note this doesn't work if the script is run from command line but other than that it functions exactly like python's
if __name__ == '__main__':
as far as I can tell
They is no way to separate them as include/include_once/require/require_once but php has get_included_files and get_required_files which is the same thing and only returns array of all included files. Its does not separate it if its required or included.
Example a.php
include 'b.php';
include_once 'c.php';
require 'd.php';
var_dump(get_required_files());
Output
array
0 => string '..\lab\stockoverflow\a.php' (length=46) <---- Returns current file
1 => string '..\lab\stockoverflow\b.php' (length=46)
2 => string '..\lab\stockoverflow\c.php' (length=46)
3 => string '..\lab\stockoverflow\d.php' (length=46)
But you can do something like
$inc = new IncludeManager($file);
var_dump($inc->find("b.php")); // Check if a file is included
var_dump($inc->getFiles("require_once")); // Get All Required Once
Class Used
class IncludeManager {
private $list = array();
private $tokens = array();
private $find;
private $file;
private $type = array(262 => "include",261 => "include_once",259 => "reguire",258 => "require_once");
function __construct($file) {
$this->file = $file;
$this->_parse();
}
private function _parse() {
$tokens = token_get_all(file_get_contents($this->file));
for($i = 0; $i < count($tokens); $i ++) {
if (count($tokens[$i]) == 3) {
if (array_key_exists($tokens[$i][0], $this->type)) {
$f = $tokens[$i + 1][0] == 371 ? $tokens[$i + 2][1] : $tokens[$i + 1][1];
$this->list[] = array("pos" => $i,"type" => $this->type[$tokens[$i][0]],"file" => trim($f, "\"\'"));
}
}
}
}
public function find($find) {
$finds = array_filter($this->list, function ($v) use($find) {
return $v['file'] == $find;
});
return empty($finds) ? false : $finds;
}
public function getList() {
return $this->list;
}
public function getFiles($type = null) {
$finds = array_filter($this->list, function ($v) use($type) {
return is_null($type) ? true : $type == $v['type'];
});
return empty($finds) ? false : $finds;
}
}
get_included_files() return array where 0 index mean first "included" file. Because direct run mean "include" in this terms, you can simple check first index for equality for __FILE__:
if(get_included_files()[0] == __FILE__){
do_stuff();
}
This can not work on PHP 4, because PHP 4 not add run file in this array.
Here's a different idea.
Just include the file whenever you need it.
Inside the include file you can decide whether it needs to include the contents:
<?php
if (defined("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE"))
return;
define("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE", 1);
// Rest of code goes here
Working solution:
$target_file = '/home/path/folder/file.php'; // or use __FILE__
if ($x=function($e){return str_replace(array('\\'), '/', $e);}) if(in_array( $x($target_file), array_map( $x , get_included_files() ) ) )
{
exit("Hello, already included !");
}
I don't think get_included_files is the perfect solution, what if your main script included some other scripts before the check? My suggestion is to check whether __FILE__ equals realpath($argv[1]):
<?php
require('phpunit/Autoload.php');
class MyTests extends PHPUnit_Framework_TestCase
{
// blabla...
}
if (__FILE__ == realpath($argv[0])) {
// run tests.
}
I took a similar approach to this issue when I cam across it. The solution I found was to load each file as needed in an include_once method. Hope this helps.
$FILES = get_included_files(); // Retrieves files included as array($FILE)
$FILE = __FILE__; // Set value of current file with absolute path
if(!in_array($FILE, $FILES)){ // Checks if file $FILE is in $FILES
include_once "PATH_TO_FILE"; // Includes file with include_once if $FILE is not found.
}
I have the following function established to check files loaded:
ARRAY_DUMP($FILES);
function ARRAY_DUMP($array){
echo "
<span style='font-size:12px;'>".date('h:i:s').":</span>
<pre style='font-size:12px;'>", print_r($array, 1), "</pre>
";
}
Output:
currentArray
(
[0] => /home/MY_DOMAIN/hardeen/index.php
[1] => /home/MY_DOMAIN/hardeen/core/construct.php
[2] => /home/MY_DOMAIN/hardeen/core/template.php
[3] => /home/MY_DOMAIN/hardeen/bin/tags.php
[4] => /home/MY_DOMAIN/hardeen/bin/systemFunction.php
)
It's sooo simple..
I have made something like this:
//code for file.php
if (!isset($file_included)){
echo "It was loaded!";
} else {
echo "It was included!";
}
//code for loader.php
//proves that atleast loader.php has loaded,
//not the file we targeted first..
$file_included = true;
include("../file.php");
And that's it.. as simple as in python.
After searching the web for hours you are my last hope:
I have to build a system which reads sent and incoming mails from a Microsoft Exchange Server. I found the following script for that:
Exchange-Web-Services-for-PHP (Heartspring)
https://github.com/Heartspring/Exchange-Web-Services-for-PHP
The existing get_messages() function returns all messages for a folder, for example "inbox". So far everything is clear. My problem starts when I want to get all messages from "sent" - Folder - i've tried many words, from "send" to "Sent Items"; without any result (mailbox not available)
My idea was to get all subfolders for the folder "root" and wrote this:
include "init.php";
$ec = new ExchangeClient();
$ec->init("bambullis#123.de", "", NULL, "https://amxprd3610.outlook.com/EWS/Services.wsdl");
$folders = $ec->get_subfolders("root");
foreach($folders as $folder) {
print_r($folder);
}
This is what I get:
stdClass Object
(
[FolderId] => stdClass Object
(
[Id] => AAAeAGJhbWJ1bGxpc0BzdHVrZSbi5kZQAuAAAAAABw352p5E4yS5voYF9ELBmiAQBXYPdO6NZAQ6T9C3xviT7xAAAAC1iXAAA=
[ChangeKey] => AQAAABYAAABXYPdO6NZAQ6T9C3xviAALNCey
)
[DisplayName] => Oberste Ebene des Informationsspeichers
[TotalCount] => 0
[ChildFolderCount] => 16
[UnreadCount] => 0
)
(I know that FolderId->Id is base64 encoded, I've modified the string above for security reasons ;o))
Now I tried to list the subfolders for this directory (I added a mailbox to see, if the value "ChildFolderCount" will change, it does):
...
print_r($folder);
print_r($ec->get_subfolders($folder->FolderId->Id));
...
This is the error I get:
The request failed schema validation: The 'Id' attribute is invalid
What did I do wrong? How to get all subfolders from "root" recursively? Thanks to this lovely guy who can help me!
The EWS-PHP get_subfolders method uses by default a TraversalType "Shallow", so it searches only the identified folder and returns only the folder IDs for items that have not been deleted.
To search in all subfolders of the identified parent folder and return only the folder IDs for items that have not been deleted you should use the "Deep" TraversalType.
For example:
<?php
include "init.php";
class myExchangeClient extends ExchangeClient {
public function get_subfolders_deep($ParentFolderId = "inbox", $Distinguished = TRUE) {
$this->setup();
$FolderItem = new stdClass();
$FolderItem->FolderShape = new stdClass();
$FolderItem->ParentFolderIds = new stdClass();
$FolderItem->FolderShape->BaseShape = "Default";
/*
** See http://msdn.microsoft.com/en-us/library/exchange/exchangewebservices.folderquerytraversaltype(v=exchg.140).aspx
** Deep Traversal: Searches in all subfolders of the identified parent folder and returns only the folder IDs for items that
** have not been deleted.
*/
$FolderItem->Traversal = "Deep";
if ($Distinguished) {
$FolderItem->ParentFolderIds->DistinguishedFolderId = new stdClass();
$FolderItem->ParentFolderIds->DistinguishedFolderId->Id = $ParentFolderId;
} else {
$FolderItem->ParentFolderIds->FolderId = new stdClass();
$FolderItem->ParentFolderIds->FolderId->Id = $ParentFolderId;
}
$response = $this->client->FindFolder($FolderItem);
if ($response->ResponseMessages->FindFolderResponseMessage->ResponseCode == "NoError") {
$folders = array();
if (!is_array($response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder)) {
$folders[] = $response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
} else {
$folders = $response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
}
return $folders;
} else {
$this->lastError = $response->ResponseMessages->FindFolderResponseMessage->ResponseCode;
}
}
}
$ec = new myExchangeClient();
$ec->init("bambullis#123.de", "", NULL, "https://amxprd3610.outlook.com/EWS/Services.wsdl");
$folders = $ec->get_subfolders_deep("root");
echo "<pre>".print_r($folders,true)."</pre>\n";
?>
Anyway, looking at the ExchangeClient class source code, the FolderID for the sent items should be "sentitems".