×

constructorsphp

关于constructorsphp的信息

admin admin 发表于2023-03-25 01:28:10 浏览45 评论0

抢沙发发表评论

本文目录一览:

Deprecated: Methods with the same name as their class will not be constructors

访问项目报错

Deprecated: Methods with the same name as their class will not be constructors

1、错误原因:

    由于PHP7开始P不再支持与类名相同的构造方法,构造方法统一使用 __construct(),导致网站在PHP5.6版本下开发的网站不能正常运行。

2、解决方法:

?php

/**验证码*/

class Soapmsg

{

    /**

     * +----------------------------------------------------------

     * 构造函数

     * +----------------------------------------------------------

     */

    function Soapmsg()

    {

    }

}

更改为:

?php

/**验证码*/

class Soapmsg

    /**

     * +----------------------------------------------------------

     * 构造函数

     * +----------------------------------------------------------

     */

    function __construct()

    {

    }

}

在PHP中遍历对象用什么?

其实百度一下就知道

我们知道,php中,foreach可以很方便地对可迭代结构(例如数组,再如对象)进行迭代操作:

[php] view plaincopy

foreach( $array as $elem){

var_dump($elem);

}

[php] view plaincopy

foreach($obj as $key=$value){

echo "$key=$value".PHP_EOL;

}

因而我们想:如果对于一个实例化对象,对其进行foreach操作,会发生什么事情呢?

首先我们定义的基础类为:

[php] view plaincopy

Class Test{

/* one public variable */

public $a;

public $b;

/* one private variable */

private $c;

public function __construct(){

$this-a = "public";

$this-b = "public";

$this-c = "private";

}

public function traverseInside(){

foreach($this as $key=$value){

echo $key."=".$value.EOL;

}

}

}

然后我们实例化该类,对其进行迭代,并与内部迭代的结果进行比较:

[php] view plaincopy

$test = new Test;

echo "hr";

echo "traverse outside:".EOL;

foreach( $test as $key=$value ){

echo $key."=".$value.EOL;

}

echo "hr";

echo "traverse inside:".EOL;

$test-traverseInside();

迭代的结果为:

可以看出:外部foreach循环的结果,只是将对象的公有属性(public)循环出来了,而对于私有属性(private),外部foreach是无法循环出来的。因而我们如果想要在外部通过foreach循环出类的所有的属性(公有的和私有的),仅仅依靠foreach是不行的,必须要对类进行“改造”。如何对类进行改造呢?如果你了解foreach的实现(参考laruence的博客:),那么可以很轻松地找到相应的方案。另外一方面,《设计模式-可复用面向对象软件设计的基础》中也提到:通过将对象的访问和遍历从对象中分离出来并放入一个迭代器对象中,迭代器模式可以实现以不同的方式对对象进行遍历。我们暂时不去深挖这句话的意思,只要知道,使用迭代器可以对对象进行遍历即可。-constructorsphp

PHP手册预定义接口部分指出:要实现迭代器模式,需要在可迭代对象中实现如下接口:

[php] view plaincopy

abstractpublicmixedcurrent( void )

abstractpublicscalarkey( void )

abstractpublicvoidnext( void )

abstractpublicvoidrewind( void )

abstractpublicbooleanvalid( void )

有了这个。实现迭代器模式就很方便了,一个简单的实例如下:

[php] view plaincopy

class TestIterator implements Iterator {

private $point = 0;

private $data = array(

"one","two","three",

);

public function __construct() {

$this-point = 0;

}

function rewind() {

$this-point = 0;

}

function current() {

return $this-data[$this-point];

}

function key() {

return $this-point;

}

function next() {

++$this-point;

}

function valid() {

return isset($this-data[$this-point]);

}

}

$it = new TestIterator;

foreach($it as $key = $value) {

echo $key, $value;

echo "\n";

}

当然,使用了迭代器的对象可以以如下方式进行遍历:

[php] view plaincopy

$it = new TestIterator;

$it-rewind();

while ($it-valid()){

$key = $it-key();

$value = $it-current();

echo "$key=$value";

$it-next();

}

最后附上YII中ListIterator(顾名思义,实现对List的迭代操作的迭代器)的实现:

[php] view plaincopy

?php

/**

* CListIterator class file.

*

* @author Qiang Xue qiang.xue@gmail.com

* @link

* @copyright Copyright © 2008-2011 Yii Software LLC

* @license

*/

/**

* CListIterator implements an interator for {@link CList}.

*

* It allows CList to return a new iterator for traversing the items in the list.

*

* @author Qiang Xue qiang.xue@gmail.com

* @version $Id$

* @package system.collections

* @since 1.0

*/

class CListIterator implements Iterator

{

/**

* @var array the data to be iterated through

*/

private $_d;

/**

* @var integer index of the current item

*/

private $_i;

/**

* @var integer count of the data items

*/

private $_c;

/**

* Constructor.

* @param array $data the data to be iterated through

*/

public function __construct($data)

{

$this-_d=$data;

$this-_i=0;

$this-_c=count($this-_d);

}

/**

* Rewinds internal array pointer.

* This method is required by the interface Iterator.

*/

public function rewind()

{

$this-_i=0;

}

/**

* Returns the key of the current array item.

* This method is required by the interface Iterator.

* @return integer the key of the current array item

*/

public function key()

{

return $this-_i;

}

/**

* Returns the current array item.

* This method is required by the interface Iterator.

* @return mixed the current array item

*/

public function current()

{

return $this-_d[$this-_i];

}

/**

* Moves the internal pointer to the next array item.

* This method is required by the interface Iterator.

*/

public function next()

{

$this-_i++;

}

/**

* Returns whether there is an item at current position.

* This method is required by the interface Iterator.

* @return boolean

*/

public function valid()

{

return $this-_i$this-_c;

}

}

综合应用数据库知识和用面向对象编程技术在PHP程序中创建出学生类

PHP使用面向对象的编程方式来编写数据库操作类

步骤1:创建一个PHP的页面“config.php”定义数据库相关的参数

?php // config.php

define(?DB_USER?, "username");

define(?DB_PASSWORD?, "password");

define(?DB_DATABASE?, "database name");

define(?DB_SERVER?, "ip address of database server");

?

第2步:创建一个PHP的类,用于连接数据库,命名为“db_connect.php”

?php // db_connnect.php

class DB_Connect {

private $con;

// constructor

function __construct() {

// connecting to database

$this-con = $this-connect();

}

//Function to connect with database

private function connect() {

// import database connection variables

require_once __DIR__.?/config.php?;

try {

$conn = new PDO(?mysql:host=?.DB_SERVER .?;

dbname=?.DB_DATABASE, DB_USER, DB_PASSWORD);

$conn-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch(PDOException $e) {

echo ?ERROR: ? . $e-getMessage();

}

return $conn;

}

public function getDbConnection(){

return $this-con;

}

}

?

第3步:创建一个类,它包含了所有的功能,为您实现SQL查询,命名为“db_functions.php”

调用里面的函数进行SQL查询、以促进可重用性和可维护性

?php // db_functions.php

class DB_Functions {

private $con;

// constructor

function __construct() {

require_once __DIR__.?/db_connect.php?;

// connecting to database

$db = new DB_Connect();

$this-con = $db-getDbConnection();

}

public function selectUser($id) {

try {

$stmt = $this-con-prepare(?SQL语句?);

$params = array(?:id? = $id);

$stmt-execute($params);

return $stmt;

} catch(PDOException $e) {

echo ?ERROR: ? . $e-getMessage();

}

}

public function otherSQLfunction($parameter) {

// other sql code

}

}

第4步:最后,在你其他的PHP文件里面只需要简单地调用“db_functions.php”的方法

?php

require_once __DIR__.?/db_functions.php?;

$db = new DB_Functions();

$result = $db-selectUser($id);

// other code

?

关于二级菜单的问题

[CODE]

HTML

headtitletest/title

script

var isDOM = (document.getElementById ? true : false);

var isIE4 = ((document.all !isDOM) ? true : false);

var isNS4 = (document.layers ? true : false);

var popTimer = 0;// Hide timeout.

// Array showing highlighted menu items.

var litNow = new Array();

function getRef(id) {

if (isDOM) return document.getElementById(id);

if (isIE4) return document.all[id];

if (isNS4) return document.layers[id];

}

function getSty(id) {

return (isNS4 ? getRef(id) : getRef(id).style);

}

function popOver(menuNum, itemNum) {

clearTimeout(popTimer);

hideAllBut(menuNum);

litNow = getTree(menuNum, itemNum);

changeCol(litNow, true);

targetNum = menu[menuNum][itemNum].target;

if (targetNum 0) {

thisX = parseInt(menu[menuNum][0].ref.left) + parseInt(menu[menuNum][itemNum].ref.left);

thisY = parseInt(menu[menuNum][0].ref.top) + parseInt(menu[menuNum][itemNum].ref.top);

with (menu[targetNum][0].ref) {

left = parseInt(thisX + menu[targetNum][0].x);

top = parseInt(thisY + menu[targetNum][0].y);

visibility = 'visible';

}

}

}

function popOut(menuNum, itemNum) {

if ((menuNum == 0) !menu[menuNum][itemNum].target)

hideAllBut(0)

else

popTimer = setTimeout('hideAllBut(0)', 500);

}

function getTree(menuNum, itemNum) {

// Array index is the menu number. The contents are null (if that menu is not a parent)

// or the item number in that menu that is an ancestor (to light it up).

itemArray = new Array(menu.length);

while(1) {

itemArray[menuNum] = itemNum;

// If we've reached the top of the hierarchy, return.

if (menuNum == 0) return itemArray;

itemNum = menu[menuNum][0].parentItem;

menuNum = menu[menuNum][0].parentMenu;

}

}

// Pass an array and a boolean to specify colour change, true = over colour.

function changeCol(changeArray, isOver) {

for (menuCount = 0; menuCount changeArray.length; menuCount++) {

if (changeArray[menuCount]) {

newCol = isOver ? menu[menuCount][0].overCol : menu[menuCount][0].backCol;

// Change the colours of the div/layer background.

with (menu[menuCount][changeArray[menuCount]].ref) {

if (isNS4) bgColor = newCol;

else backgroundColor = newCol;

}

}

}

}

function hideAllBut(menuNum) {

var keepMenus = getTree(menuNum, 1);

for (count = 0; count menu.length; count++)

if (!keepMenus[count])

menu[count][0].ref.visibility = 'hidden';

changeCol(litNow, false);

}

// *** MENU CONSTRUCTION FUNCTIONS ***

function Menu(isVert, popInd, x, y, width, overCol, backCol, borderClass, textClass) {

// True or false - a vertical menu?

this.isVert = isVert;

// The popout indicator used (if any) for this menu.

this.popInd = popInd

// Position and size settings.

this.x = x;

this.y = y;

this.width = width;

// Colours of menu and items.

this.overCol = overCol;

this.backCol = backCol;

// The stylesheet class used for item borders and the text within items.

this.borderClass = borderClass;

this.textClass = textClass;

// Parent menu and item numbers, indexed later.

this.parentMenu = null;

this.parentItem = null;

// Reference to the object's style properties (set later).

this.ref = null;

}

function Item(text, href, frame, length, spacing, target) {

this.text = text;

this.href = href;

this.frame = frame;

this.length = length;

this.spacing = spacing;

this.target = target;

// Reference to the object's style properties (set later).

this.ref = null;

}

function writeMenus() {

if (!isDOM !isIE4 !isNS4) return;

for (currMenu = 0; currMenu menu.length; currMenu++) with (menu[currMenu][0]) {

// Variable for holding HTML for items and positions of next item.

var str = '', itemX = 0, itemY = 0;

// Remember, items start from 1 in the array (0 is menu object itself, above).

// Also use properties of each item nested in the other with() for construction.

for (currItem = 1; currItem menu[currMenu].length; currItem++) with (menu[currMenu][currItem]) {

var itemID = 'menu' + currMenu + 'item' + currItem;

// The width and height of the menu item - dependent on orientation!

var w = (isVert ? width : length);

var h = (isVert ? length : width);

// Create a div or layer text string with appropriate styles/properties.

// Thanks to Paul Maden ([url][/url]) for helping debug this in IE4, apparently

// the width must be a miniumum of 3 for it to work in that browser.

if (isDOM || isIE4) {

str += 'div id="' + itemID + '" style="position: absolute; left: ' + itemX + '; top: ' + itemY + '; width: ' + w + '; height: ' + h + '; visibility: inherit; ';-constructorsphp

if (backCol) str += 'background: ' + backCol + '; ';

str += '" ';

}

if (isNS4) {

str += 'layer id="' + itemID + '" left="' + itemX + '" top="' + itemY + '" width="' + w + '" height="' + h + '" visibility="inherit" ';-constructorsphp

if (backCol) str += 'bgcolor="' + backCol + '" ';

}

if (borderClass) str += 'class="' + borderClass + '" ';

// Add mouseover handlers and finish div/layer.

str += 'onMouseOver="popOver(' + currMenu + ',' + currItem + ')" onMouseOut="popOut(' + currMenu + ',' + currItem + ')"';-constructorsphp

// Add contents of item (default: table with link inside).

// In IE/NS6+, add padding if there's a border to emulate NS4's layer padding.

// If a target frame is specified, also add that to the a tag.

str += 'table width="' + (w - 8) + '" border="0" cellspacing="0" cellpadding="' + (!isNS4 borderClass ? 3 : 0) + '"trtd align="left" height="' + (h - 7) + '"' + 'a class="' + textClass + '" href="' + href + '"' + (frame ? ' target="' + frame + '"' : '') + text + '/a/td';-constructorsphp

if (target 0) {

// Set target's parents to this menu item.

menu[target][0].parentMenu = currMenu;

menu[target][0].parentItem = currItem;

// Add a popout indicator.

if (popInd) str += 'td class="' + textClass + '" align="right"' + popInd + '/td';

}

str += '/tr/table' + (isNS4 ? '/layer' : '/div');

if (isVert) itemY += length + spacing;

else itemX += length + spacing;

}

if (isDOM) {

var newDiv = document.createElement('div');

document.getElementsByTagName('body').item(0).appendChild(newDiv);

newDiv.innerHTML = str;

ref = newDiv.style;

ref.position = 'absolute';

ref.visibility = 'hidden';

}

// Insert a div tag to the end of the BODY with menu HTML in place for IE4.

if (isIE4) {

document.body.insertAdjacentHTML('beforeEnd', 'div id="menu' + currMenu + 'div" ' + 'style="position: absolute; visibility: hidden"' + str + '/div');-constructorsphp

ref = getSty('menu' + currMenu + 'div');

}

// In NS4, create a reference to a new layer and write the items to it.

if (isNS4) {

ref = new Layer(0);

ref.document.write(str);

ref.document.close();

}

for (currItem = 1; currItem menu[currMenu].length; currItem++) {

itemName = 'menu' + currMenu + 'item' + currItem;

if (isDOM || isIE4) menu[currMenu][currItem].ref = getSty(itemName);

if (isNS4) menu[currMenu][currItem].ref = ref.document[itemName];

}

}

with(menu[0][0]) {

ref.left = x;

ref.top = y;

ref.visibility = 'visible';

}

}

// Syntaxes: *** START EDITING HERE, READ THIS SECTION CAREFULLY! ***

//

// menu[menuNumber][0] = new Menu(Vertical menu? (true/false), 'popout indicator', left, top,

// width, 'mouseover colour', 'background colour', 'border stylesheet', 'text stylesheet');

//

// Left and Top are measured on-the-fly relative to the top-left corner of its trigger, or

// for the root menu, the top-left corner of the page.

//

// menu[menuNumber][itemNumber] = new Item('Text', 'URL', 'target frame', length of menu item,

// additional spacing to next menu item, number of target menu to popout);

//

// If no target menu (popout) is desired, set it to 0. Likewise, if your site does not use

// frames, pass an empty string as a frame target.

//

// Something that needs explaining - the Vertical Menu setup. You can see most menus below

// are 'true', that is they are vertical, except for the first root menu. The 'length' and

// 'width' of an item depends on its orientation -- length is how long the item runs for in

// the direction of the menu, and width is the lateral dimension of the menu. Just look at

// the examples and tweak the numbers, they'll make sense eventually :).

var menu = new Array();

// Default colours passed to most menu constructors (just passed to functions, not

// a global variable - makes things easier to change later in bulk).

var defOver = '#336699', defBack = '#003366';

// Default 'length' of menu items - item height if menu is vertical, width if horizontal.

var defLength = 22;

// Menu 0 is the special, 'root' menu from which everything else arises.

menu[0] = new Array();

// A non-vertical menu with a few different colours and no popout indicator, as an example.

// *** MOVE ROOT MENU AROUND HERE *** it's positioned at (5, 0) and is 17px high now.

menu[0][0] = new Menu(false, '', 5, 0, 17, '#669999', '#006666', '', 'itemText');

// Notice how the targets are all set to nonzero values...

// The 'length' of each of these items is 40, and there is spacing of 10 to the next item.

// Most of the links are set to '#' hashes, make sure you change them to actual files.

menu[0][1] = new Item(' File', '#', '', 40, 10, 1);

menu[0][2] = new Item(' Edit', '#', '', 40, 10, 2);

menu[0][3] = new Item(' Help', '#', '', 40, 10, 3);

// An example of a link with a target frame/window as well...

menu[0][4] = new Item(' Site', '', '_new', 40, 10, 6);

menu[0][5] = new Item(' Other', '', '_new', 40, 10, 8);

// File menu.

menu[1] = new Array();

// The File menu is positioned 0px across and 22 down from its trigger, and is 80 wide.

// All text in this menu has the stylesheet class 'item' -- see the style section above.

// We've passed a 'greater-than' sign '' as a popout indicator. Try an image...?

menu[1][0] = new Menu(true, '', 0, 22, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[1][1] = new Item('Open', '#', '', defLength, 0, 0);

menu[1][2] = new Item('Save', '#', '', defLength, 0, 0);

// Non-zero target means this will trigger a popout -- menu[4] which is the 'Reopen' menu.

menu[1][3] = new Item('Reopen', '#', '', defLength, 0, 4);

menu[1][4] = new Item('Exit', '#', '', defLength, 0, 0);

// Edit menu.

menu[2] = new Array();

menu[2][0] = new Menu(true, '', 0, 22, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[2][1] = new Item('Cut', '#', '', defLength, 0, 0);

menu[2][2] = new Item('Copy', '#', '', defLength, 0, 0);

menu[2][3] = new Item('Paste', '#', '', defLength, 0, 0);

// Help menu

menu[3] = new Array();

menu[3][0] = new Menu(true, '', 0, 22, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[3][1] = new Item('Contents', '#', '', defLength, 0, 0);

menu[3][2] = new Item('Index', '#', '', defLength, 0, 0);

menu[3][3] = new Item('About', '#', '', defLength, 0, 5);

// Reopen menu

menu[4] = new Array();

menu[4][0] = new Menu(true, '', 80, 0, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[4][1] = new Item('site1', '#', '', defLength, 0, 0);

menu[4][2] = new Item('site2', '#', '', defLength, 0, 0);

menu[4][3] = new Item('site3', '#', '', defLength, 0, 0);

// Help About popout

menu[5] = new Array();

// Leftwards popout with a negative x and y relative to its trigger.

menu[5][0] = new Menu(true, '', -85, -17, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[5][1] = new Item('Leftwards!brAnd up!', '#', '', 40, 0, 0);

menu[6] = new Array();

menu[6][0] = new Menu(true, '', 0, 22, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[6][1] = new Item('site1', '#', '', defLength, 0, 0);

menu[6][2] = new Item('site2', '#', '', defLength, 0, 7);

menu[6][3] = new Item('site3', '#', '', defLength, 0, 0);

menu[7] = new Array();

menu[7][0] = new Menu(true, '', 80, 0, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[7][1] = new Item('site21', '#', '', defLength, 0, 0);

menu[7][2] = new Item('site22', '#', '', defLength, 0, 0);

menu[7][3] = new Item('site23', '#', '', defLength, 0, 0);

menu[8] = new Array();

menu[8][0] = new Menu(true, '', 0, 22, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[8][1] = new Item('other1', '#', '', defLength, 0, 0);

menu[8][2] = new Item('other2', '#', '', defLength, 0, 0);

menu[8][3] = new Item('other3', '#', '', defLength, 0, 9);

menu[9] = new Array();

menu[9][0] = new Menu(true, '', 80, 0, 80, defOver, defBack, 'itemBorder', 'itemText');

menu[9][1] = new Item('other1', '#', '', defLength, 0, 0);

menu[9][2] = new Item('other2', '#', '', defLength, 0, 0);

menu[9][3] = new Item('other3', '#', '', defLength, 0, 0);

// *** OPTIONAL CODE FROM HERE DOWN ***

// These two lines handle the window resize bug in NS4. See body onResize="...".

// I recommend you leave this here as otherwise when you resize NS4's width menus are hidden.

var popOldWidth = window.innerWidth;

nsResizeHandler = new Function('if (popOldWidth != window.innerWidth) location.reload()');

// This is a quick snippet that captures all clicks on the document and hides the menus

// every time you click. Use if you want.

if (isNS4) document.captureEvents(Event.CLICK);

document.onclick = clickHandle;

function clickHandle(evt)

{

if (isNS4) document.routeEvent(evt);

hideAllBut(0);

}

// This is just the moving command for the example.

function moveRoot()

{

with(menu[0][0].ref) left = ((parseInt(left) 100) ? 100 : 5);

}

// End --

/script

!-- *** IMPORTANT STYLESHEET SECTION - Change the border classes and text colours *** --

style

!--

.itemBorder { border: 1px solid black }

.itemText { text-decoration: none; color: #FFFFFF; font: 12px Tahoma,Arial, Helvetica }

.crazyBorder { border: 2px outset #663399 }

.crazyText { text-decoration: none; color: #FFCC99; font: Bold 12px Tahoma,Arial, Helvetica }

--

/style

/head

body marginwidth="0" marginheight="0" style="margin: 0" onLoad="writeMenus()" onResize="if (isNS4) nsResizeHandler()"-constructorsphp

table bgcolor="#006666" width="100%" border="0" cellpadding="0" cellspacing="0"

trtd height="17"font size="1" /font/td/tr/table

/body

/HTML

[/CODE]

php怎么实例化有依赖注入的类

PHP依赖注入的理解。分享给大家供大家参考,具体如下:

看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了,现在确实很懒变得!-constructorsphp

首先,我们假设,我们要开发一个组件命名为SomeComponent。这个组件中现在将要注入一个数据库连接。

在这个例子中,数据库连接在component中被创建,这种方法是不切实际的,这样做的话,我们将不能改变数据库连接参数及数据库类型等一些参数。

class SomeComponent {

/**

* The instantiation of the connection is hardcoded inside

* the component so is difficult to replace it externally

* or change its behavior

*/

public function someDbTask()

{

$connection = new Connection(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "invo"

));

// ...

}

}

$some = new SomeComponent();

$some-someDbTask();

为了解决上面所说的问题,我们需要在使用前创建一个外部连接,并注入到容器中。就目前而言,这看起来是一个很好的解决方案:

class SomeComponent {

protected $_connection;

/**

* Sets the connection externally

*/

public function setConnection($connection)

{

$this-_connection = $connection;

}

public function someDbTask()

{

$connection = $this-_connection;

// ...

}

}

$some = new SomeComponent();

//Create the connection

$connection = new Connection(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "invo"

));

//Inject the connection in the component

$some-setConnection($connection);

$some-someDbTask();

现在我们来考虑一个问题,我们在应用程序中的不同地方使用此组件,将多次创建数据库连接。使用一种类似全局注册表的方式,从这获得一个数据库连接实例,而不是使用一次就创建一次。

class Registry

{

/**

* Returns the connection

*/

public static function getConnection()

{

return new Connection(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "invo"

));

}

}

class SomeComponent

{

protected $_connection;

/**

* Sets the connection externally

*/

public function setConnection($connection){

$this-_connection = $connection;

}

public function someDbTask()

{

$connection = $this-_connection;

// ...

}

}

$some = new SomeComponent();

//Pass the connection defined in the registry

$some-setConnection(Registry::getConnection());

$some-someDbTask();

现在,让我们来想像一下,我们必须在组件中实现两个方法,首先需要创建一个新的数据库连接,第二个总是获得一个共享连接:

class Registry

{

protected static $_connection;

/**

* Creates a connection

*/

protected static function _createConnection()

{

return new Connection(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "invo"

));

}

/**

* Creates a connection only once and returns it

*/

public static function getSharedConnection()

{

if (self::$_connection===null){

$connection = self::_createConnection();

self::$_connection = $connection;

}

return self::$_connection;

}

/**

* Always returns a new connection

*/

public static function getNewConnection()

{

return self::_createConnection();

}

}

class SomeComponent

{

protected $_connection;

/**

* Sets the connection externally

*/

public function setConnection($connection){

$this-_connection = $connection;

}

/**

* This method always needs the shared connection

*/

public function someDbTask()

{

$connection = $this-_connection;

// ...

}

/**

* This method always needs a new connection

*/

public function someOtherDbTask($connection)

{

}

}

$some = new SomeComponent();

//This injects the shared connection

$some-setConnection(Registry::getSharedConnection());

$some-someDbTask();

//Here, we always pass a new connection as parameter

$some-someOtherDbTask(Registry::getConnection());

到此为止,我们已经看到了如何使用依赖注入解决我们的问题。不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。但是从长远来看,这种形式的依赖注入也有一些缺点。-constructorsphp

例如,如果组件中有较多的依赖关系,我们需要创建多个setter方法传递,或创建构造函数进行传递。另外,每次使用组件时,都需要创建依赖组件,使代码维护不太易,我们编写的代码可能像这样:

//Create the dependencies or retrieve them from the registry

$connection = new Connection();

$session = new Session();

$fileSystem = new FileSystem();

$filter = new Filter();

$selector = new Selector();

//Pass them as constructor parameters

$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);

// ... or using setters

$some-setConnection($connection);

$some-setSession($session);

$some-setFileSystem($fileSystem);

$some-setFilter($filter);

$some-setSelector($selector);

我想,我们不得不在应用程序的许多地方创建这个对象。如果你不需要依赖的组件后,我们又要去代码注入部分移除构造函数中的参数或者是setter方法。为了解决这个问题,我们再次返回去使用一个全局注册表来创建组件。但是,在创建对象之前,它增加了一个新的抽象层:-constructorsphp

class SomeComponent

{

// ...

/**

* Define a factory method to create SomeComponent instances injecting its dependencies

*/

public static function factory()

{

$connection = new Connection();

$session = new Session();

$fileSystem = new FileSystem();

$filter = new Filter();

$selector = new Selector();

return new self($connection, $session, $fileSystem, $filter, $selector);

}

}

这一刻,我们好像回到了问题的开始,我们正在创建组件内部的依赖,我们每次都在修改以及找寻一种解决问题的办法,但这都不是很好的做法。

一种实用和优雅的来解决这些问题,是使用容器的依赖注入,像我们在前面看到的,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖可以使我们的代码耦合度更低,很好的降低了组件的复杂性:

class SomeComponent

{

protected $_di;

public function __construct($di)

{

$this-_di = $di;

}

public function someDbTask()

{

// Get the connection service

// Always returns a new connection

$connection = $this-_di-get('db');

}

public function someOtherDbTask()

{

// Get a shared connection service,

// this will return the same connection everytime

$connection = $this-_di-getShared('db');

//This method also requires a input filtering service

$filter = $this-_db-get('filter');

}

}

$di = new Phalcon\DI();

//Register a "db" service in the container

$di-set('db', function(){

return new Connection(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "invo"

));

});

//Register a "filter" service in the container

$di-set('filter', function(){

return new Filter();

});

//Register a "session" service in the container

$di-set('session', function(){

return new Session();

});

//Pass the service container as unique parameter

$some = new SomeComponent($di);

$some-someTask();

现在,该组件只有访问某种service的时候才需要它,如果它不需要,它甚至不初始化,以节约资源。该组件是高度解耦。他们的行为,或者说他们的任何其他方面都不会影响到组件本身。我们的实现办法

Phalcon\DI 是一个实现了服务的依赖注入功能的组件,它本身也是一个容器。

由于Phalcon高度解耦,Phalcon\DI 是框架用来集成其他组件的必不可少的部分,开发人员也可以使用这个组件依赖注入和管理应用程序中不同类文件的实例。

基本上,这个组件实现了 Inversion of Control 模式。基于此,对象不再以构造函数接收参数或者使用setter的方式来实现注入,而是直接请求服务的依赖注入。这就大大降低了整体程序的复杂性,因为只有一个方法用以获得所需要的一个组件的依赖关系。-constructorsphp

此外,这种模式增强了代码的可测试性,从而使它不容易出错。

在容器中注册服务

框架本身或开发人员都可以注册服务。当一个组件A要求调用组件B(或它的类的一个实例),可以从容器中请求调用组件B,而不是创建组件B的一个实例。

这种工作方式为我们提供了许多优点:

我们可以更换一个组件,从他们本身或者第三方轻松创建。

在组件发布之前,我们可以充分的控制对象的初始化,并对对象进行各种设置。

我们可以使用统一的方式从组件得到一个结构化的全局实例

服务可以通过以下几种方式注入到容器:

//Create the Dependency Injector Container

$di = new Phalcon\DI();

//By its class name

$di-set("request", 'Phalcon\Http\Request');

//Using an anonymous function, the instance will lazy loaded

$di-set("request", function(){

return new Phalcon\Http\Request();

});

//Registering directly an instance

$di-set("request", new Phalcon\Http\Request());

//Using an array definition

$di-set("request", array(

"className" = 'Phalcon\Http\Request'

));

在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。

容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。

在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。

用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。

Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。-constructorsphp

//Register a service "db" with a class name and its parameters

$di-set("db", array(

"className" = "Phalcon\Db\Adapter\Pdo\Mysql",

"parameters" = array(

"parameter" = array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "blog"

)

)

));

//Using an anonymous function

$di-set("db", function(){

return new Phalcon\Db\Adapter\Pdo\Mysql(array(

"host" = "localhost",

"username" = "root",

"password" = "secret",

"dbname" = "blog"

));

});

以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:

$di-setParameter("db", 0, array(

"host" = "localhost",

"username" = "root",

"password" = "secret"

));

从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:

$request = $di-get("request");

或者通过下面这种魔术方法的形式调用:

$request = $di-getRequest();

Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。

具体的 Phalcon\Http\Request 请求示例:

$request = $di-getShared("request");

参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:

$component = $di-get("MyComponent", array("some-parameter", "other"));

php依赖注入是在构造函数中注入吗

PHP 依赖注入

tags: dependency injection,php

by Ryan on January 8, 2009

Dependency injection is the answer to more maintainable, testable, modular code.

依赖注入是对于要求更易维护,更易测试,更加模块化的代码的答案。

Every project has dependencies and the more complex the project is the more dependencies it will most likely have. The most common dependency in today’s web application is the database and chances are if it goes down the app will all together stop working. That is because the code is dependent on the database server… and that is perfectly fine. Not using a database server because it could one day crash is a bit ridiculous. Even though the dependency has its flaws, it still makes life for the code, and thus the developer, a lot easier.-constructorsphp

每个项目都有依赖(外界提供的输入), 项目越复杂,越需要更多的依赖。在现今的网络应用程序中,最常见的依赖是数据库,其风险在于,一旦数据库暂停运行,那么整个程序也因此而停止运行。这是因为代码依赖数据库服务器。。。这已足够。因为数据库服务器有时会崩溃而弃用它是荒谬的。尽管依赖有其自身的瑕疵,它仍然使代码,因而也使程序开发人员的生活更容易些。-constructorsphp

The problem with most dependencies its the way that code handles and interacts with them, meaning, the problem is the code and not the dependency. If you are not using dependency injection, chances are your code looks something like this:-constructorsphp

对于大多依赖而言,问题在于代码如何处理它们及与它们交往。也就是说,问题是代码本身而非依赖。如果你没有使用依赖注入,有机会遇到下列代码:

class Book {

publicfunction __construct(){

$registry = RegistrySingleton::getInstance();

$this-_databaseConnection=$registry-databaseConnection;

// or

global$databaseConnection;

$this-_databaseConnection=$databaseConnection;

}

}

The book object now is given full access to the database once it is constructed. That is good, the book needs to be able to talk to the database and pull data. The problem lies in the way the book gained its access. In order for the book to be able to talk to the database the code must have an outside variable named $databaseConnection, or worse, it must have a singleton pattern class (registry) object containing a record for a databaseConnection. If these don’t exist the book fails, making this code is far from modular.-constructorsphp

现在对象book一旦建立,就已经完全连到数据库。这没什么不好,book需要跟数据库交谈并取得数据。问题在于book取得其接入的方法。要使book能够与数据库交谈,代码必须有一个外来的变量$databaseConnect, 更糟糕的是,它必须有个singleton类型类(registry)的对象,其包含一个databaseConnection的记录。如果这些不存在的话,book会无法运行,这就使得这些代码远远不够模块化。-constructorsphp

This raises the question, how exactly does the book get access to the database? This is where inversion of control comes in.-constructorsphp

这就提出一个问题,到底book如何接入数据库?这就是IoC出现的原因了。

In Hollywood a struggling actor does not call up Martin Scorsese and ask for a role in his next film. No, the opposite happens. Martin Scorsese calls up the broke actor and asks him to play the main character in his next movie. Objects are struggling actors, they do not get to pick the roles they play, the director needs to tell them what to do. Objects do not get to pick the outside systems they interact with, instead, the outside systems are given to the objects. Remember this as Inversion of Control: The Hollywood Way.-constructorsphp

在好莱坞,一个濒临绝境的演员不会打电话给Martin Scoresese要求在他的下个影片中扮演一个角色。绝不会这样,实际正相反。Martin Scorese 会打电话给这个困顿的演员并邀请他在下一部影片中扮演重要的角色。对象是挣扎中的演员,他们不会去捡自己所扮演的角色,导演需要告诉他们去做什么。对象不会到它所接触的外界挑选系统,相反,外界系统会被赋予对象。记住这就是IoC( Inversion of Control):好莱坞的做法。-constructorsphp

This is how a developer tells his objects how to interact with outside dependencies:

如下是开发人员告诉他的对象如何与外界的依赖打交道:

class Book {

publicfunction __construct(){}

publicfunction setDatabaseConnection($databaseConnection){

$this-_databaseConnection=$databaseConnection;

}

}

$book=new Book();

$book-setDatabase($databaseConnection);

This code allows for the book class to be used in any web app. The Book is no longer dependent on anything other than the developer supplying a database shortly after object creation.这代码允许这个book类能在任何的网页程序中使用。此book不再依赖任何事项,除了程序开发者要在对象创建后立即要提供一个数据库。-constructorsphp

This is, at its finest, dependency injection. There are two common practices of injecting dependencies. The first being constructor injection and the second being setter injection. Constructor injection involves passing all of the dependencies as arguments when creating a new object. The code would look something like this:-constructorsphp

这就是最佳方法—依赖注入。有两种常用的依赖注入的方式。一种是 constructor (注:构造函数。这种译法似乎并不恰当,类中此方法更多是用来对某些属性进行初始化)注入,一种是setter 注入。Constructor注入涉及到将所有依赖作为参数,传递给新创建的对象。看起来像这样:-constructorsphp

$book=new Book($databaseConnection,$configFile);

The more dependencies an object has, the messier this construction becomes. There are other reasons why this is a bad approach, involving ideas around code reusability and constructors doing work.-constructorsphp

对象的依赖越多,construction就会变得越杂乱。这里还有其他的原因为什么这是一个坏方法,其涉及到包括代码重用和constructors要做的工作。

This leaves us with other method of dependency injection, called setting injection, which involves creating a public method inside the object for the dependencies that need injection.-constructorsphp

这就给我们留下其他的方法进行依赖注入,称为设置注入,包含在需要注入依赖的对象里创建一个公共方法:

$book=new Book();

$book-setDatabase($databaseConnection);

$book-setConfigFile($configFile);

This is easy to follow, but it leads writing more and more code for your application. When a book object is created three lines of code are required. If we have to inject another dependency, a 4th line of code is now needed. This gets messy quickly.-constructorsphp

这很容易实现,但是它会导致为您的程序写大量代码。当创建一个book对象时,需要三行代码。如果我们必须注入另外的依赖时,必须增加第四行代码。这很快变得一团糟。

The answer to this problem is a factory, which is class that is designed to create and then inject all the dependencies needed for an object. Here is an example:-constructorsphp

此问题的对策是一个工厂factory,它是一个类,用来创建然后注入一个对象的所有依赖。举例如下:

class Factory {

public static $_database;

public static function makeBook(){

$book=new Book();

$book-setDatabase(self::$_database);

// more injection...(更多注入)

return$book;

}

}

And then:

然后:

$book= Factory::makeBook();

All dependencies should be registered into the factory object during run time. This object is now the gateway that all dependencies must pass through before they can interact with any classes. In other words, this is the dependency container.-constructorsphp

所有的依赖在运行期间都应在此factory对象中注册。它现在是大门,在依赖可以与任何对象打交道前,都必经此门。

The reason makeBook is a public static function is for ease of use and global access. When I started this article off I made a reference to the singleton pattern and global access being a poor choices of code. They are… for the most part. It is bad design when they control access, but it is perfectly ok when they control creation. The makeBook function is only a shortcut for creation. There is no dependency what-so-ever between the book class and the factory class. The factory class exists so we can contain our dependencies in one location and automatically inject those dependencies with one line of code creation.-constructorsphp

makeBook作为一个静态方法的原因是易用并且可以在全局得到。在本文开头,我提出使用singleton类型和全局变量对代码而言是一个糟糕的选择。多数情形下,确实如此!当他们控制接入时,是一个差设计。但是在他们控制创建时,是一个不错的选择。makeBook方法仅仅是创建的一个捷径。在book类和factory类之间,没有任何依赖。由于factory类的存在,我们的依赖可以存放在一个地点,并且用一行创建代码就可自动注入这些依赖。-constructorsphp

The factory or container class removes all of the extra work of dependency injection.

此处的factory或者容器类移走了依赖注入的所有额外工作。

Before injection:

以前的注入:

$book=new Book();

And now:

目前的方法:

$book= Factory::makeBook();

Hardly any extra work, but tons of extra benefits.

几乎没有任何的额外工作,但是有大量益处。

When test code is run, specifically unit tests, the goal is to see if a method of a class is working correctly. Since the book class requires database access to read the book data it adds a whole layer of complexity. The test has to acquire a database connection, pull data, and test it. All of a sudden the test is no longer testing a single method in the book class, it is now also testing database. If the database is offline, the test would fail. This is FAR from the goal a unit test.当运行测试代码时,尤其是单元测试,其目标是检查类中的方法是否正确地工作。由于book类要求接入数据库并且读取图书数据,它增加了一层复杂度。此测试不得不进行数据库连接,取得数据,然后测试它。瞬间,测试不再是测试book类中的单个方法,它目前同时也测试数据库。如果数据库离线,测试就会失败。这已经远离了单元测试的目的。-constructorsphp

A way of dealing with this is just using a different database dependency for the unit tests. When the test suite starts up a dummy database is injected into the book. The dummy database will always have the data the developer expects it to have. If a live database was used in a unit test the data could potentially change causing tests to unnecessarily fail. There is no need for a unit test to be refactored when a record in a database changes.-constructorsphp

解决上述问题的方法是用不同的数据库依赖来作单元测试。当测试套件开始时,虚拟的数据库被注入book.虚拟数据库将永远拥有开发人员所期待的数据。如果使用一个真实数据库,数据的潜在变化会导致测试不必要的失败。当数据库的记录改变了,不需因此重新进行单元测试。-constructorsphp

The code is more modular because it can dropped into any other web application. Create the book object and inject a database connection with $book-setDatabase(). It does not matter if the database is in Registery::Database, $database, or $someRandomDatabaseVarible. As long as there is a database connection the book will work inside any system.-constructorsphp

此代码现在更加模块化,因为它可以被放到任何其他的网页程序。创建book对象并且用$book-setDatabase()注入数据库连接。此时,数据库在 Registery::Database, $database,或者$someRandomDatabaseVarible都无关大局。只要有一个数据库连接,book就可以在任何系统内工作。-constructorsphp

The code is more maintainable because each object given exactly what it needs. If separate database connections are required between different instances of the same class then there no extra code needed inside the class what-so-ever. Give book1 access to database1 and book2 access to database2.-constructorsphp

代码更加易维护,因为每个对象都被赋予了它的所需。如果同一类的实例需要不同的数据库连接,在类内部一点儿也不需要额外的代码。例如book1连接到数据库1,book2连接到数据库2

Factory::$_database=$ourDatabaseVarForDB1;

$book1= Factory::makeBook();

$book2= Factory::makeBook();

$book2-setDatabase($database2);

Dependency injection really is the answer to more maintainable, testable, modular code.

依赖注入真的是更易维护,更易测试和更加模块化的答案。