PHP connecting to MySQL with PDO: How to call Associative Array values within double quotes?

So essentially I'm creating a custom PHP function and class for handling PDO MySQL connections for me. I'm using this both to learn about creating my own classes/functions, and because I'd like to make it modular, i.e. I don't have to change sections of code when something may change later so it's readable (I'm still learning).

Here is the code. There may be a better way to differentiate between giving the variables and parsing the .ini, but I'm unsure how to do that in a single function, so I separated them.

<?php

class PDOMySQL
{
	var $_Creds;
	var $_PDO_Conn;
	var $_Server_Name;
	var $_DB_Name;

	function ConnFile($_Creds_File)
	{
		$_Creds = parse_ini_file($_Creds_File);
		$_PDO_Conn = new PDO // Create connection to MySQL Database.
			(
			"mysql:host=$_Creds['server'];dbname=$_Creds['database']",
			$_Creds['username'],
			$_Creds['password']
			);
		$_PDO_Conn->setAttribute
			( // Prepare Attribute for Error Mode Exception Handling.
			PDO::ATTR_ERRMODE,
			PDO::ERRMODE_EXCEPTION
			);
		$_PDO_Conn->setAttribute
			( // always disable emulated prepared statement when 
			   // using the MySQL driver
			PDO::ATTR_EMULATE_PREPARES, false
			);

		return $_PDO_Conn;
	} // Function Ends

	function ConnVar($_Server_Name,$_DB_Name,$_UserName,$_Password)
	{
		$_PDO_Conn = new PDO // Create connection to MySQL Database.
			(
			"mysql:host=$_Server_Name;dbname=$_DB_Name",
			$_UserName,
			$_Password
			);
		$_PDO_Conn->setAttribute
			( // Prepare Attribute for Error Mode Exception Handling.
			PDO::ATTR_ERRMODE,
			PDO::ERRMODE_EXCEPTION
			);
		$_PDO_Conn->setAttribute
			( // always disable emulated prepared statement when 
			   // using the MySQL driver
			PDO::ATTR_EMULATE_PREPARES, false
			);

		return $_PDO_Conn;
	} // Function Ends

} // Class Ends

?>

The important bit:

	$_PDO_Conn = new PDO // Create connection to MySQL Database.
		(
		"mysql:host=$_Creds['server'];dbname=$_Creds['database']",
		$_Creds['username'],
		$_Creds['password']
		);

My question is regarding the mysql:host line. parse_ini_file produces an associative array of whatever is set up in the .ini file. In this case, the ini file is like so:

[credentials]
server = <server>
database = <database>
username = <username>
password = <password>

How do I bypass the need for double quotes for the new PDO connection, or call the values with those keys in the array within double quotes?

As is typical, I immediately thought of how to do it after posting this thread.

		$_PDO_Conn = new PDO // Create connection to MySQL Database.
			(
			"mysql:host=" . $_Creds['server'] . ";dbname=" . $_Creds['database'],
			$_Creds['username'],
			$_Creds['password']
			);

I can be pretty air-headed sometimes. Not sure if there's a better solution, but that definitely works after testing.

there's also the {curly} syntax, which I prefer for all interpolated variables:

$dsn = "mysql:host={$_Creds['server']};dbname={$_Creds['database']};";

As an aside, you should ALWAYS set the connection charset:

$dsn .= 'charset=utf8mb4;';

And I highly recommend setting various $options on construct as well (such as setting errmode to throw exceptions, and turning emulated prepares off).

edit

as a second aside… man, y u indent like that

if (right) {
    // nice
}

if (left)
{
    // okay, whatever
}

if (whatthefuck)
    { 
        // whatthefuck
    }

Yes; the ConnFile method should handle only the ini file, and then pass the parsed credentials to ConnVar.

1 Like

Neat. I didn't know about that.

Not sure what that would be for?

Why do it the (left) way? Because then I can scan up directly and find where it starts with my eyes. Putting it at the end of the line makes that irritating to do at best. Sure, a good IDE will handle it for you, but I like my code to be legible in any way I can.

Also, the extra line gives a perfect place for comments imo. Below the starting function or variable declaration, so you can know it applies to that and only that. Having the comments above or below the entire function/variable declaration makes it ambiguous if you don't use extra line breaks, and I'd rather use less of those.

It's a difference between using {} and () along with setting a variable equal to something. {} being used for "code to be ran" vs () used for "parameters to be given to a function".

Your examples are all involving () then {} for a function. Note how all the ones in my code that are indented that way are just () after an = without any related {}.

Note how all {} are treated in my code as (left) is in your example.

Functions with long parameters need to be line broken to stay under 80 characters per line, but I wish to keep the same structure for blobs, i.e. ( being on the same column as ) for easy eye scanning while reading.

"But still, why indent like that?"

Because the point, as far as I can tell, of having {} on the same line as the function is to let you know where the ownership is. i.e.

if (stuff)
{
do stuff...
}

Is to show that everything between the {} is owned by if in that it's part of that statement and will be run within it. It's what the code within {} is being applied under, essentially. That the code runs within the if statement.

However, if you consider that () is for passing parameters in this case, it doesn't work that way.

		$_PDO_Conn->setAttribute
			( // Prepare Attribute for Error Mode Exception Handling.
			PDO::ATTR_ERRMODE,
			PDO::ERRMODE_EXCEPTION
			);

The stuff between the () isn't "owned" or "applied under" the variable $_PDO_Conn. It is applied under setAttribute. So I use the indention to show it's ownership is to the thing on the right of the -> and not the left.

I get that as an Object, it is essentially being applied under $_PDO_Conn, but think of it this way. setAttribute is normally written as $variable->setAttribute(attributes) without line breaking. The () is tied to setAttribute and not the variable (yet), so the indention is to show that.

Actually, I had a thought regarding that. I'd rather have it like so:

	function Conn($_Creds_File, $_Database)
	{
		$_Creds = parse_ini_file($_Creds_File);
		$_PDO_Conn = new PDO // Create connection to MySQL Database.
			(
			"mysql:host=" . $_Creds['server'] . ";dbname=$_Database",
			$_Creds['username'],
			$_Creds['password']
			);
		$_PDO_Conn->setAttribute
			( // Prepare Attribute for Error Mode Exception Handling.
			PDO::ATTR_ERRMODE,
			PDO::ERRMODE_EXCEPTION
			);
		$_PDO_Conn->setAttribute
			( // always disable emulated prepared statement when 
			   // using the MySQL driver
			PDO::ATTR_EMULATE_PREPARES, false
			);

		return $_PDO_Conn;
	} // Function Ends

The only thing that would change is the database being accessed. The server, username, & password should remain the same.

that's a big assumption. it would be more flexible to have named credentials in your config.

Question: is this class meant to be purely a factory (used only for making PDO's)?

yes, i realize. probably should have used a function as the example. however, ONLY "right" makes any sense for functions. the opening paren should always be associated with the function name.

someFunction( foo, bar );  // ok
someFunction(foo, bar);  // ok
someFunction (foo, bar);  // ok, even
someFunction(
    foo, 
    bar
);  // ok
someFunction
(foo, bar);  // NO.
someFunction
    (foo, bar);  // NO. NONONONONO

I'd like to emphasize that no offense is intended. and I get that you might prefer this, for yourself. and that it's syntactically valid. but if you ever need to work with other programmers, it will become a serious problem. it is extremely unusual and non-intuitive, to the point that it interrupts the thought process while trying to read the code.

again, "left" is fine. not my preference, but it's fine. : )

For specifying the charset for the connection. Otherwise, it's just assumed. This can lead to mistakes or corrupt data. There are even some obscure injection attacks which rely on the connection charset being different than the actual charset of the input data.

Hopefully, you're using utf-8 everywhere. In mysql, that's "UTF8MB4" (mysql is stupid; "UTF8" means "utf-8 but only the first three bytes").

Not sure what you mean by named credentials. My understanding is that putting them in an ini file with the correct security settings is best practice then using parse_ini_file accordingly. If the username/password/server/database change, someone has to go and modify the .ini rather than a blob of code they might not understand. However, a database can have many tables.

... And I just realized I put $_Database and not $_Table. I meant $_Table. i.e. the table is changed. Fixing that now.

I think of it in terms of "what should I be connecting to at any one point in time." One connection to a database while modifying multiple tables should be fine, but multiple databases? That should probably be left to separate transactions altogether, because it's far more likely to have different tables in my mind. i.e. you're far more likely to modify the same database but with multiple table names rather than multiple databases with the same table name (unless synchronizing data but aside from that).

I mean, my ultimate goal of this is to make it as modular as possible, but I can only go so far as I have knowledge. I can't think of an easy way to change databases without modifying the ini file since nothing would inherently give me the database name, so I'd have to hard code that in somewhere (i.e. the ini that is parsed).

Essentially, I guess, yeah. It's going to be my class for handling stuff I do with PDO to interact with MySQL Databases. I'll be making another one after this for the slightly different MS SQLSRV Driver for Microsoft SQL databases after I'm done with this one and it's tested as working.

My PHP scripts were becoming nothing but a bunch of require_onces because I'm not using PHP to actually deal with websites. It's just a scripting language I'm using for transferring and managing data between SQL databases (one MySQL and the other MS SQL, both on different machines, one remote, one local).

I've been writing and learning this for a short time, but I have realized it's best to "modularize" the code to where, if another project comes up that requires I interact with a MySQL or MS SQL database, I can just drop this mostly generic function list in and use that rather than rewriting it.

The ideal is to be that if I do write PHP for a long period of time, I'll end up with a library of script files for handling things I do a lot (like what I've mentioned here).

It's fine. I understand that like any language, formatting is important for varying reasons, but I also find most of it feels arbitrary as long as the original intent is maintained.

I see why you would say that. However, by doing it the other way, you lose the ability of using the columns where the () line up as easy cognitive ways to segment the code into understandable parts. On one hand, a ) should never come after another function (i.e. a function shouldn't really be inside a (). I can't think of any examples where there has been one), but on the other, this, to me, is less legible than my way when it's in the middle of a bunch of functions doing various things:

$_Query = ("
	INSERT INTO " . $_Table . " (" . $_Columns . ")
	VALUES (:" . $_Params . ")
	"); // SQL Query for each column with binds for prepared statement

Which, that isn't tied to a function, but it involves () and line breaks across multiple lines.

As opposed to:

$_Query = // SQL Query for each column with binds for prepared statement
	("
	INSERT INTO " . $_Table . " (" . $_Columns . ")
	VALUES (:" . $_Params . ")
	");

Or:

 $_Columns = implode(
 	", ",
 	array_keys($_Record_Dump)
 	); // Prepares columns.

As opposed to:

$_Columns = implode
	(	// Prepares columns.
	", ",
	array_keys($_Record_Dump)
	);

For me, the indention has always been to convey it's "under" the nearest above line that is less indented. I guess I don't see how it'd be a serious problem or how it would/could be unintuitive, unless the only reason is because "{} are never used that way", and that standardization makes it easier for everyone, generally speaking, when handling each others' work.

When you say assumed, do you mean there is some default it tries, or that it guesses?

So I see people suggesting that you should just append the variables to the connection string.
This is the best way IF the input is correctly sanitized.

Sanitize the fuck out of it, even if the user isn't supposed to be able to change the details. It will come back to bite you in the ass.

Another good suggestion is to use an already existing PHP library to perform MySQL queries in PHP.

Another even better suggestion: Don't use PHP... please?

meaning, if you have more than one connection, you should have more than one section in your ini file. for example,

[foo]
host=foo.example
dbname=foo
user=foouser
pass=foopassword

[bar]
host=bar.example
. . .

and not

host=foo.example
user=foouser
pass=foopassword

; database name is somewhere else

"table"? you don't specify a table name in your dsn.

this is, indeed, good.

I was mainly trying to determine if this was more of a "factory" or a "call this every time you need a $pdo". The latter purpose is flawed.

this doesn't need (and shouldn't have) parenthesis at all. It makes the whole statement look like a mistake (like you meant to do something else).

in a case where you have arguments that don't fit comfortably in a function's arg list, it's better to assign them separately, beforehand.

this worries me. if you aren't using an actual query builder, then just write the entire query. piecing it together from args is error-prone (especially since it carries the implicit assumption that the args are correct ).

actually... i'm not entirely sure. i think it defaults to latin-1 (which is pretty much ISO 8859-1). can't find docs though.

that was only to allow me to focus on the part i was talking about, instead of re-writing the whole thing. yes, it should be done at-once.

it's from an ini file. it is not user input.

as a general rule, if it were user input, sanitization is not a good approach. validate, and reject anything that doesn't pass outright. never try to "fix" user input.

yes. i find PDO more than capable + suitable for anything. but if you want a more-abstracted library, look into existing ones before building one yourself. as i mentioned above, if this class if just a factory for PDOs, that's fine. but if it's intended for more than that, you shouldn't be re-inventing the wheel.

bleh, troll.

: p

From a secuity standpoint, you are taking the paranoid approach. An example is you wouldn't want to reject the string "I wasn't there" because the ' might cause some issues. You sanatize it.

There are tools and functions embedded in PHP specifically to deal with this common problem.

no; you escape the ' character.

I wasnt there    // sanitized
I wasn''t there  // escaped

of course, what you should be doing in the case of sql queries, is neither: you should be passing data as parameters. And now, the ' is no longer any different than any other character, and you have no problems.

no. "security" is not paranoia.
in the vast majority of cases, "security" is nothing more than code which works correctly, on purpose.

the reason for rejecting over fixing is that you're extremely likely to fix it wrong. in any nontrivial situation, there's an edge case you didn't think of. Same reason you should favor whitelists over blacklists.

Firstly, Sanitization modifies the input to ensure that it is valid (such as doubling single quotes).
http://download.oracle.com/oll/tutorials/SQLInjection/html/lesson1/les01_tm_ovw3.htm

Secondly you are correct, you should be using parameters in an SQL query, but not everything is an SQL query.

My point was, is that you should make sure that everything being parsed to any dangerous functions (Any output, database operations, shell executions, file operations, basically anything that can have an exploiut) should be properly sanitized. Which in my eyes also includes escaping.

agreed; we're on the same page.

If you're going to make the distinction (and i take the position that you should), then "sanitize" has a "clean up" meaning, and "escape" has an "encode" meaning. "escape" also more strictly requires an awareness of context (i.e., you cannot "escape" something without knowing the context: it's different for sql vs. html vs. whatever) while "sanitize" usually addresses the value on its own, independently from context (e.g., sanitizing a numeric input would involve removing any character which is not a digit, decimal, +, -, e, etc.: taking those things that would cause validation to fail, and getting rid of them). but yes, loosely, one could be considered a superset or the other.

the distinction is also useful for php specifically (php's FILTER_SANITIZE_* flags, for example, work by filtering out unwanted characters, whereas the FILTER_VALIDATE_* flags give you the value on success and false (or null) otherwise). I've found that, especially with beginners, conflating the two leads to confusion and really horrible code.

I use sanitization as a generic input for either: Making the Input Sane, or safe.

So in my eyes that includes escaping, but it seems that I have been somewhat miss-using it causing some misconnunication

I implied that you should properly: Sanitize and escape all input, regardless of where it's coming from.

Which just stops a bunch of small exploits being combined to cause some issues.

That's what PDO with the MySQL Driver is in this case, right? Or do you mean some extension to PDO?

I mean, the point has been to make it so that I write less code to do the same things and make it so it "just works".

I had intended to do just that, but wasn't sure how to call separate sections. Then I realized that would require separate functions to work if I did call specific values from the file. I'm probably just not aware of how you'd properly do that though. Maybe ask for the section in the function arguments and just use variables?

You are right. I keep mixing up the two things that can change at the moment. Because we're talking about making the code modular, but I'm thinking of the specific use case at the moment which requires different databases and tables as well.

I guess I'm not understanding what a "factory" is then. Again, the ideal I'm going for is that this simplifies my usage of PDO to access databases. Instead of remembering how to do it with each type, I just have a class for each one that is as simple as calling Conn -> calling Query/Execute and being done with it. Not that I'll ever use more than MySQL and MS SQL, but it would also make it more efficient for me than copying from another example and adjusting every time to fit my needs.

To me, that sounds like the latter thing. "Call this every time you need a $pdo anything."

Wow, you're right. I just got that from reading examples on W3Schools and php.net.

>researches what a query builder is

Neat. I will most likely start using that then.

A thing though. Obviously this is preferred when I don't know what the args will be, but the original use case for this code was essentially moving data from a remote MySQL database to a local MS SQL database. So "sanitizing" wasn't really something I was worried about.

Though, I would be putting any actual changes using args through as transactions->commits, which as I understand it, escapes characters accordingly on the server itself.

Like, the code I've written so far is simply the following:

  1. connect to remote MySQL Database
  2. run query to get records from table
  3. connect to local MS SQL Database
  4. create query to insert records retrieved
  5. run query to insert records into MS SQL Database

So like, I haven't had to worry about user input.

^^^
From memory PDO is bad, although it has been almost 2 years since I did anything in PHP that required a database.

no, would mean something like Doctrine or Eloquent. I'm not saying you need something like this, but if you want it, use one that already exists. there's a lot to cover and a lot to get wrong doing it on your own.

ini files can have different sections (it would create an array when parsed). I prefer json, however.

a factory simply builds your PDO instance. it has nothing to do with how your code gets that instance or passes it around where needed. If you're only doing ETL stuff in basic-ish scripts, then the line might blur and not matter quite so much.

php.net is the official manual and a very good reference. however, be aware of two things:
- the examples are meant to demonstrate some particular aspect of whatever topic is at hand. they are not meant as copy+paste resources. for the most part, it's not code that would actually be useful in real code.
- the comment section is all user-submitted. it is not vetted. votes up or down is not necessarily a good indication as to the quality of the comment.

STAY AWAY from w3schools. lessons there will hurt you, not help.

not transactions, but prepared statements using parameters. this keeps the data separate from the query. no need for escaping, no risk of injection (accidental or not: you can screw up your own db even easier than an attacker can).

There are two APIs available in php for mysql.

  • mysqli ("improved") is completely capable, but can be awkward to use and has a steep learning curve.

  • PDO is a very, very nice tool. it's powerful, and easy to use and learn. and since this involves both mysql and mssql, it's pretty much the de facto choice.

you may also have heard of ext/mysql (the mysql_*() functions). It's been The Wrong Choice for over ten years (and is finally gone in php 7).


@Vitalius you might be interested in http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers and https://phpdelusions.net/pdo . If you use IRC, there's lots of helpful people with good advice at freenode##php .

1 Like

I do remember using PDO, and I remember it being trash

1 Like

Dunno what to tell you. it was kinda weird when it first came out, or if you had the old mysql drivers. but neither of those have been issues for 5-8 years plus.