Getting PHP variable name as string for many variables - php

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);
}
}
}

Related

Creating a translation class with PHP associative array

I have written a very simple translation class that is supposed to return the meaning associated with the phrase that I give to it. Under the hood, it loads translations from a csv upon construction into an associative array. Upon translation request, it checks the array. If the phrase is there as a key in the array, returns its value, which is its translation. If the phrase does not exist as a key, it loads the array from the file again (as there might be new translations), checks for the key again. If it does not find the key again, the phrase will be returned as is.
<?php
class Translate{
function __construct() {
$this->loadTranslations();
}
public function get($message, $lang = "de"): string{
if(key_exists($message, self::$de)){
return self::$de[$message];
}
else {
//Load translations again
$this->loadTranslations();
if(isset(self::$de[$message])){
return self::$de[$message];
}
else {
return $message;
}
}
}
protected static $de = [];
protected function loadTranslations() {
$file = fopen(__DIR__ . "/../data/de.csv", "r");
if($file){
while($line = fgets($file)){
$en_de = explode(":", $line);
self::$de[array_shift($en_de)] = array_shift($en_de);
}
}
fclose($file);
}
}
$t = new Translate();
echo $t->get("Hello") . PHP_EOL;
Content of de.csv is like this:
"Hi": "Hallo"
"Hello": "Hallo"
The problem is when asked for a translation, the class always returns the given phrase. When I dump the array, the phrase is there as a key, but there is no success in accessing $array[$phrase] as PHP does not find the key in the array!
The problem is that in your CSV file, you have quotes round the text, so although Hello exists, it's actually stored in the translation array as "Hello" so will not match.
You could either redo your translation file to not have the quotes, or you could use the functionality of fgetcsv() to read it and strip out any surrounding quotes (use : as the separator)...
protected function loadTranslations() {
$file = fopen(__DIR__ . "/a.csv", "r");
if($file){
while([$key, $trans] = fgetcsv($file, null, ":", '"')){
self::$de[$key] = $trans;
}
}
fclose($file);
}
Just looking at the code to fetch the translation, you could shorten it. First check that the translations are loaded, then return the translation - using ?? to say if it's not found, then return the original message...
public function get($message, $lang = "de"): string{
if(!isset(self::$de)){
$this->loadTranslations();
}
return self::$de[$message] ?? $message;
}
Your csv looks more like json to me.
I'd probably adjust the file to be json permanently, but until then, just convert it into a json string manually, then decode it to create your key-value pairs.
self::$de = json_decode(
'{' . implode(',', file(__DIR__ . "/a.csv")) . '}',
true
);
In other words, make all of your language files valid json. This way you can instantly cal json_decode() on the entire file contents and the array is ready. Keeping your file in the current format means individually isolating each line of text in the file and calling a function to parse it -- this is waaaaay too much work to be done each time.
Please consistently write your class variables at the top of your class.
$de should not be a variable name -- I am assuming it is referring to a specific language. $lang() should be used to specify the user's desired language and search for the appropriate filename.
Edit:
I really can't overstate how beneficial it is to convert your files to valid json -- it just makes everything cleaner. Here is a re-write of your code. I don't agree with the use of a static class variable, nor the constructor that that loads a language without know what is going to be used. And as previously mentioned there should be no variable that refers to a specific language ($de). The class variable $translations should be an associative array containing subarrays so that you can permanently load and access multiple translations at the same time.
Untested suggestion:
class Translate{
protected $translations = [];
protected function loadTranslations($lang)
{
$filePath = __DIR__ . '/' . $lang . '.json';
if (file_exists($filePath)) {
$this->translations[$lang] = json_decode(file_get_contents($filePath), true);
}
}
public function get($message, $lang = "de"): string
{
if (!isset($this->translations[$lang])) {
$this->loadTranslations($lang);
}
return $this->translations[$lang][$message] ?? $message;
}
// e.g. $newTrans = ['Good Day' => 'Guten Tag', ...]
public function set($lang, $newTrans)
{
if (!isset($this->translations[$lang])) {
$this->loadTranslations($lang);
}
$this->translations[$lang] += $newTrans; // insert or overwrite key-value pair(s)
file_put_contents(__DIR__ . '/' . $lang . '.json', json_encode($this->translations[$lang])); // commit to file
}
}
$t = new Translate();
echo $t->get("Hello") . PHP_EOL;

there a more effective/elegant way to config parsing?

example:
$CONF = parse_ini_file('cfg.ini', true);
$EmailUN = $CONF['EM']['key01'];
$EmailPW = $CONF['EM']['key02'];
$EmailTO = $CONF['EM']['key03'];
$SMSAPI = $CONF['SMS']['key01'];
$SMSUN = $CONF['SMS']['key02'];
$SMSPW = $CONF['SMS']['key03'];
$SMSNUM = $CONF['SMS']['key04'];
is there a more effective/elegant way to import this data? i want to learn best practices while im still learning. i dont want to fill up the whole top of my php doc with objects calling for keys. if this is a duplicate i apologize in advance.
One approach would be a helper function that accesses your configuration.
If for example you wanted to access your configuration like this:
config('SMS.key01');
You could define the config function as:
function config(string $key)
{
static $config = parse_ini_file('cfg.ini', true);
$curr = $config;
foreach(explode('.', $key) as $segment) {
if(!isset($curr[$segment]) {
return null;
} else if(is_array($curr[$segment])) {
$curr = $curr[$segment];
continue;
}
return $curr[$segment];
}
}
The above is a minimal example roughly based on Laravel's config and array_get helpers. Your helper function would likely need to be implemented differently in the context of your application or framework.
The main advantage of this approach is that all of the implementation details of how your configuration is stored and accessed is contained within the function. What you are doing right now bleeds low level implementation details into the rest of your code.

Best practice for tiny code reuse in PHP

For a long time I have a problem - should I reuse small parts of code and if so, how should I do it so it would be the best practice.
What I mean about small code is for example:
if (!is_array($table)) {
$table = array($table);
}
or
$x = explode("\n", $file_content);
$lines = array();
for ($i=0, $c = count($x); $i<$c; ++$i) {
$x[$i] = trim($x[$i]);
if ($x[$i] == '') {
continue;
}
$lines[] = $x[$i];
}
Such tiny parts of code may be used in many classes in one project but some of them are used also in many projects.
There are many possible solutions I think:
create simple function file and put them all reusable piece of codes as function, include them simple in project and use them whenever I want
create traits for those piece of codes and use them in classes
reuse code by simple copy paste or creating function in specific class (??)
other ?
I think all of those solutions have their pros and cons.
Question: What method should I use (if any) to reuse such code and why is this approach the best one in your opinion?
I think that "the best way" depends on many factors including the technology your applications use (procedural, OOP), versions of PHP they run on, etc. For example, traits are interesting and useful but they are available only since php 5.4.0 so using this tool to group your code snippets you will not be able to reuse them in systems running on earlier PHP versions. On the other hand if your app uses an OOP style and you organized your resuable small code snippets in functions, their usage may seem awkward in an OOP app and conflict with the function names in a particular class. In this case I think grouping your functions in classes would seem more natural.
Putting everything together, it seems that classes provide better tool for grouping resuable code snippets in terms outline above, namely backward compatibility with earlier PHP versions, avoiding function names conflicts, etc.) Personally I code mostly in OOP, so i have a Util class where I group small functions representing resuable pieces of code snippets that do not directly relate to each other and thus could not be logically groupped in other classes.
As mentioned already traits are a good thing. But might be a bit hard to manage after a while, and it might not be supported everywhere since its new.
What I do is to create Tool classes that have a lot small static functions, like:
class ArrayTools
{
static public function CheckArray($array)
{
if (!is_array($array))
{
$array = array($array);
}
return $array;
}
}
So you can call it with ArrayTools::CheckArray($array)
Please go with traits if your code mainly involves classes and objects.. As the the concept of traits exclusively focusses on code reuse ability.
Following are the code snippets which actually I use with Plain PHP projects, these code snippets are used from various frameworks good traits and best practices.
1.
The following code is used to check the environment in which your working, based on the environment you can set the some global variables, error reporting as so on.
if(!defined('ENVIRONMENT')){
define('ENVIRONMENT','DEVELOPMENT');
}
if (defined('ENVIRONMENT'))
{
switch (ENVIRONMENT)
{
case 'DEVELOPMENT':
case 'TESTING':
$base_url = 'http://localhost/project_name/';
error_reporting(E_ALL);
break;
case 'PRODUCTION':
$base_url = 'http://hostname/project_name/';
error_reporting(0);
break;
default:
exit('The application environment is not set correctly.');
}
}
2.
/* This function is used to PRINT the ARRAY data in the pre formatted manner */
if (!function_exists('pr')) {
function pr($data) {
echo '<pre>', print_r($data), '</pre>';
}
}
3.
/* This function is used to Sanitize the user data and make data safe to insert into the database */
function sanitize($data) {
global $link;
$data = trim($data);
return htmlentities(strip_tags(mysqli_real_escape_string($link, $data)));
}
4.
/* Used to get the difference of 2 arrays
Returns the array with difference
*/
function multi_diff($arr1,$arr2){
$result = array();
foreach ($arr1 as $k=>$v){
if(!isset($arr2[$k])){
$result[$k] = $v;
} else {
if(is_array($v) && is_array($arr2[$k])){
$diff = multi_diff($v, $arr2[$k]);
if(!empty($diff))
$result[$k] = $diff;
}
}
}
return $result;
}
5.
/* This fnction is used to generate the random keys of specific length
Accepts parameter of certain length if not specified it will generate 20 bit length automatically
*/
function generate_random_key($length = 20) {
//Initializing the varialble
$keystring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
$random_key = '';
for ($i = 0; $i < $length; $i++) {
$random_key.=$keystring[rand(0, strlen($keystring) - 1)];
}
//Return the randomly generated key
return $random_key;
}
6.
/* This function outputs the errors in ul>li format with unstyled
* To get the bullets styling remove class='list-unstyled' in <ul> tag */
function output_errors($errors){
$output = array();
foreach ($errors as $error) {
$output[] = '<li>'.$error.'</li>';
}
return '<ul class="list-unstyled">'.implode('', $output).'</ul>';
}
7.
/* Checks whether the user is loggedin else will redirect to the protectect page */
function protected_page(){
if(is_loggedin() === false){
// header('Location: protected.php');
header('Location: logout.php');
exit();
}
}
8.
/* If user tries to access the page directly accessing through the URL,
* If already loggedin then redirect him to any of the inner page
*/
function login_redirect(){
if(is_loggedin() === true){
header('Location: home.php');
}
}
9.
/* This function is used to check whether the user exists or not */
function email_exists($email){
/* Your Code */
}
/* This function is used to check whether the user isActive or not */
function is_active($email){
/* Your Code */
}
/* This function will get the userid from the email */
function userid_from_email($email) {
/* Your Code */
}
/* This fucntion is used to login the user based on the email-id and password */
function login($email,$password){
/* Your Code */
}
/* Check whether the USER is loggedin or not */
function is_loggedin(){
return (isset($_SESSION['userid'])) ? true : false;
}
Hope this helps you. Cheers!

PHP APC problems when storing objects

I am trying to build an configuration parser for my application I installed APC today, but everytime I try to put an serialized object in the store, it does not get in there and does not. (I am checking with apc.php for my version[3.1.8-dev] on PHP 5.3.16 [My Dev Environment], so I am sure that the data is not in the cache). this is how I pass the data to the cacher:
// The data before the caching
array (
'key' => md5($this->filename),
'value' => serialize($this->cfg)
);
// The caching interface
function($argc){
$key = $argc['key'];
Cache\APC::getInstance()->set($key,$argc['value']);
}
// The caching method described above
public function set($key, $val) {
if (apc_exists($key)) {
apc_delete ($key);
return apc_store($key, $val);
}
else
return false;
}
// the constructor of the configuration class.
// It 1st looks for the configuration in
// the cache if it is not present performs the reading from the file.
public function __construct($filename = '/application/config/application.ini',
$type = self::CONFIG_INI)
{
if (defined('SYSTEM_CACHE') && SYSTEM_CACHE === 'APC'){
$key = md5($filename);
$cfg = APC::getInstance()->get($key);
if (!empty($cfg)) {
print "From Cache";
$this->cfg = unserialize($cfg);
return;
} else {
print "From File";
}
}
}
I did a few tests and there is not a problem with the MD5() key (which I thought while writing this question) nor with APC itself. I am really stuck on this one, nothing odd in the logs, so if anyone can give me at least some directions will be very appreciated.
Thanks in advance!
The problem is was in my code:\
public function set($key, $val) {
/*
*
* If the key exists in the cache delete it and store it again,
* but how could it exist when the else clause is like that...
*/
if (apc_exists($key)) {
apc_delete ($key);
return apc_store($key, $val);
}
// This is very wrong in the current case
// cuz the function will never store and the if will
// never match..
else
return false;
}
NOTE:
Always think and keep your eyes open, if you still can't find anything get off the PC and give yourself a rest. Get back after 10-15 minutes and pown the code. It helps! :D

FOSRestBundle adds 'S' in get URL

// MySomethingController.php
// look no s
public function getSomethingAction($args)
{
...
}
// routing.yml
my_something:
type: rest
resource: Blah\Bundle\BlahBundle\Controller\MySomethingController
running:
php app/console router:debug
Output:
[router] Current routes
Name Method Pattern
get_something GET /somethings/{args}.{_format}
Why is the route 'somethings' ( plural with an 's' ) instead of 'something'?
is this a setting I have somewhere? or is this expected?
after digging in the code:
https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/Routing/Loader/Reader/RestActionReader.php
Here it is:
private function generateUrlParts(array $resources, array $arguments)
{
$urlParts = array();
foreach ($resources as $i => $resource) {
// if we already added all parent routes paths to URL & we have
// prefix - add it
if (!empty($this->routePrefix) && $i === count($this->parents)) {
$urlParts[] = $this->routePrefix;
}
// if we have argument for current resource, then it's object.
// otherwise - it's collection
if (isset($arguments[$i])) {
if (null !== $resource) {
$urlParts[] =
strtolower(Pluralization::pluralize($resource))
.'/{'.$arguments[$i]->getName().'}';
} else {
$urlParts[] = '{'.$arguments[$i]->getName().'}';
}
} elseif (null !== $resource) {
$urlParts[] = strtolower($resource);
}
}
return $urlParts;
}
I've opened an issue:
https://github.com/FriendsOfSymfony/FOSRestBundle/issues/247
in hopes that this would become optional
This is an update answer based on the ticket opened by #phillpafford.
Ismith77 commented on the ticket and I thought explained very well why:
Without the pluralization we wouldn't know the relationship between
the methods which is going to be important for example when
implementing #52. furthermore its a key idea of REST that we have the
GET for a single element in a collection be in a "subdir" of the
collection itself.
So if you do "proper" REST then /member/{id}.{_format} would be oddly
named but it would be actually wrong if your collection itself
wouldn't then also reside under /member{.format}.
The gist of all of this is .. the solution as is isn't so much about
convenience than it is about enforcing people actually following REST
principles.
PS: However I would like to point out that when you have a word like "data" which is plural on it's own this is a bit annoying...

Categories